English Русский 中文 Español 日本語 Português
Die statistische Verteilung in Form von einem Histogramm ohne Indikator-Puffer und Arrays

Die statistische Verteilung in Form von einem Histogramm ohne Indikator-Puffer und Arrays

MetaTrader 5Beispiele | 22 Dezember 2016, 09:27
1 321 0
Sergey Pavlov
Sergey Pavlov

Einführung

Das Histogramm — das Instrument, das optisch ermöglicht die Verteilung der statistischen Daten zu bewerten, die nach der Frequenz ihres Treffens in einem bestimmten Intervall (im Voraus gegebenen) gruppiert.

Die Konstruktion der Histogramme und ihre Verwendung in der Analyse der statistischen Daten — ist ein gut studiertes Thema, dem eine Menge der Artikel [1, 2, 3, 4, 5, 6, 7] gewidmet sind und nach der die ausreichende Anzahl der Beispiele in CodeBase [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] erstellt wurden. Jedoch werden die verwendeten Algorithmen basierend auf der Verwendung der Indikator-Puffer oder Arrays sein. Im vorliegenden Artikel ist die Aufbausmöglichkeit der statistischen Verteilungen verschiedener Markt-Charakteristiken ohne komplizierte Berechnungen betrachtet, Sortierungen, Auswahl usw. Dafür verwenden wir das "graphische" Gedächtnis — dem Abschnitt, wo die Eigenschaften der graphischen Objekte gespeichert werden. Es handelt sich darum, dass beim Aufbau der benutzerdefinierten Histogramme so oder so die graphischen Objekte verwendet werden, also warum nutzen wir dann ihre "verborgenen" Möglichkeiten auf die volle Macht nicht und das vorhandene reiche Funktional?

Das Ziel des vorliegenden Artikels — die einfachen Lösungen der standardmäßigen statistischen Aufgaben anzubieten. Der Schwerpunkt wird auf der Visualisierung der statistischen Verteilungen und ihrer Hauptcharakteristiken sein. Auf die Interpretation der Histogramme und ihre Zweckmäßigkeit lassen wir uns nicht ablenken.

Das Prinzip des Aufbaus der Histogramme

Das Histogramm stellt ein Kolonnen-Diagramm der Frequenzen dar. Nach einer der Achsen verschieben die Werte der Variable, und nach dem anderem — die Treffens-Frequenz (das Entstehen, das Erscheinen u.ä.) dieser Werte. Die Höhe jeder Kolonne zeigt die Frequenz (die Anzahl) der Werte, die dem entsprechenden Intervall zugehörig sind, gleich der Breite der Kolonne. Meistens werden solche Diagramme horizontal dargestellt, d.h., die Werte der Variable sind auf der horizontalen Achse, und der Frequenz — auf der vertikalen. Die Verwendung der Histogramme für die Darstellung der untersuchten Informationen ermöglicht den statistischen Daten die Anschaulichkeit und die Ausdruckskraft zu geben, es macht einfacher die Wahrnehmung, und bei vielen Fällen auch die Analyse.

Im vorliegenden Artikel werden wir auf die vertikalen Histogramme der Variationsreihen betrachten: die Preiswerte der untersuchten Charakteristiken werden sich auf der vertikalen Achse in aufsteigender Reihenfolge befinden, und der Frequenz — auf der horizontalen Achse (in Abb. 1). Die ins Terminal kommenden Preisdaten werden verteilt und werden auf der laufenden Bar gruppiert und es können verhältnismäßig seiner Achse links dargestellt, rechts oder gleichzeitig von beiden Seiten.

in Abb. 1. Das vertikale Histogramm der Verteilung der Preise Bid und Ask 

in Abb. 1. Das vertikale Histogramm der Verteilung der Preise Bid und Ask

Betrachten wir eine konkrete Aufgabe:

  • Der Aufbau eines Histogramms der Verteilung der Preise Bid und Ask; 
  • Die Preis-Daten Ask setzen wir rechts von der laufenden Bar, und Bid - links davon;
  • Beim Ankommen eines neuen Ticks werden wir die Frequenzen für jeden kommenden Wert des Preises rechnen, d.h. das Intervall des Histogramms ist der Größe des Punktes des laufenden Instruments gleich.

Jetzt werden wir die Bedingung komplizieren: man darf nicht mehr die Indikator-Puffer, die Arrays oder die Strukturen verwenden.

Wie kann das Problem gelöst werden? 

Erstens muss man erdenken, wo die angesammelten Frequenzen für jede Kolonne des Histogramms speichern. Sogar auf in Abb. 1. Es ist sichtbar, dass solche Kolonnen zahlreich sein können, und das Erste was im Kopf einfällt, ist das Array zu verwenden. Übrigens ein dynamisches Array, da die mögliche Preisbreite (die Anzahl der Kolonnen) auf der gewählten vorübergehenden Stelle des Graphik im Voraus unbekannt ist. Aber laut dem Vertrag über die Aufgabe wird es verboten.

Zweitens muss man die Aufgabe der Suche und der Sortierung lösen: wo und wie die Daten für die Umrechnung und das Abzeichnen des Histogramms gesucht werden müssen.

Es stellt sich heraus, dass die Entwickler der Sprache MQL5 schon lange das Funktional erstellt wurde, welches nötig für die Lösung der gestellten Aufgabe ist, übrigens ein ziemlich starkes Funktional. Ja, sehr geehrte Leser, es wird vorgeschlagen, die "verborgenen" (nicht auffälligen) Möglichkeiten der Funktionen-Gruppen der graphischen Objekte zu verwenden. Jedes Objekt hat Eigenschaften - die sind solche Variablen, die zusammen mit dem Objekt erstellt werden und dienen für die Speicherung einer Menge verschiedener Parameter. Einige Eigenschaften kann man nicht ganz zweckmäßig verwenden, und dabei wird die Funktionalität nicht verloren sein. Wir nennen solche Eigenschaften als "das graphische Gedächtnis". Mit anderen Worten, wenn wir eine irgendwelche Variable speichern müssen und beim Bedarf ihren Wert bekommen, erstellen wir das graphische Objekt und ordnen einer bestimmten Eigenschaft den Wert der Variable zu.

Die Eigenschaften der graphischen Objekte es ist eine Art der Analoge der globalen Variable des Terminales, nur besser. Die globalen Variablen existieren im Clientterminal 4 Wochen nach der Zeit des letzten Aufrufs zu ihm, und später werden sie automatisch gelöscht. Das graphische Gedächtnis existiert solange, bis das graphische Objekt nicht entfernt wird. Stellen Sie sich vor, welche Möglichkeiten es uns öffnet? 

Welche Eigenschaften der graphischen Objekte man im graphischen Gedächtnis einsetzen kann

Bei der Erstellung eines beliebigen graphischen Objektes muss ihm einen einzigartigen Namen geben, und es ist eine Textzeile. Die Zeile kann aus Unterzeilen bestehen, und die Unterzeile kann die Hauptarten der formatierten Daten enthalten: die ganze, logische, mit dem Gleitkomma, die Farbe, das Datum und die Zeit. Das heißt, die Eigenschaft OBJPROP_NAME passt für die Speicherung der Variablen, allerdings, hauptsächlich für das Lesen der Daten.

Die nächste Eigenschaft, die man benutzen kann - ist die Beschreibung des Objektes OBJPROP_TEXT. Das ist auch eine Textzeile, deren Möglichkeiten wesentlich höher sind, als bei der vorherigen Eigenschaft. In diesem Feld der Eigenschaft ist es möglich sowie das Lesen, auch das Schreiben der Variablen.

Jeder grafischer Objekt hat Koordinaten: der Preis OBJPROP_PRICE und die Zeit OBJPROP_TIME. Sie können auch im grafischen Gedächtnis verwendet werden.

Zurück zu unserer Aufgabe. Die Frequenzen werden wir in der Eigenschaft OBJPROP_TEXT speichern, und der Wert der Preise Bid und Ask — in der Eigenschaft OBJPROP_NAME. Der Kode der Funktion, die die Objekte erstellt und die Frequenzen sammelt, wurde unten aufgeführt:

void DrawHistogram(bool draw,     // links oder rechts zeichnen wir das Histogramm
                   string h_name, // die einzigartige Präfix für den Namen des Objektes
                   double price,  // Preis (der untersuchte Parameter)
                   datetime time, // die Verknüpfung des Histogramms zur laufenden Bar
                   int span,      // span des untersuchten Parameters 
                   int swin=0)    // das Fenster des Histogramms
  {
   double y=NormalizeDouble(price,span);
   string pfx=DoubleToString(y,span);
// wenn draw=true ist, dann zeichnen wir das Histogramm von rechts
   if(draw)
     {
      string name="+ "+h_name+pfx;                   // der Objektname: Präfix+Preis
      ObjectCreate(0,name,OBJ_TREND,swin,time,y);    // das Objekt erstellen
      ObjectSetInteger(0,name,OBJPROP_COLOR,color_R_active); // Geben wir die Farbe des Objektes ein
      ObjSet;                                                // Das Makro für die Kürzung des Codes
      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)
        {// Wenn der erhaltende Preis in der Auswahl das erste Mal geraten ist
         ObjectSetString(0,name,OBJPROP_TEXT,"*1");          // Die Frequenz für den vorliegenden Preis ist 1
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize); // So haben wir die Koordinate der Zeit bestimmt
        }
      else
        {// Wenn der erhaltende Preis in der Auswahl nicht das erste Mal geraten ist
         string str=ObjectGetString(0,name,OBJPROP_TEXT);    // So bekommen wir der Wert der Eigenschaft
         string strint=StringSubstr(str,1);                  // Wir wählen die Unterzeile aus
         long n=StringToInteger(strint);                     // wir haben die Frequenz für die weiteren Rechnungen bekommen
         n++;                                                // wir haben den Wert um 1 vergrößert
         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n); // wir haben die Eigenschaft des neuen Wertes geschrieben
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time+hsize*n);//So haben wir die Koordinate der Zeit bestimmt
        }
     }
// wenn draw=false ist, dann zeichnen wir das Histogramm von links
   if(!draw)
     {
      string name="- "+h_name+pfx;
      ObjectCreate(0,name,OBJ_TREND,swin,time,y);
      ObjectSetInteger(0,name,OBJPROP_COLOR,color_L_active);
      ObjSet;
      if(StringFind(ObjectGetString(0,name,OBJPROP_TEXT),"*",0)<0)
        {
         ObjectSetString(0,name,OBJPROP_TEXT,"*1");
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize);
        }
      else
        {
         string str=ObjectGetString(0,name,OBJPROP_TEXT);
         string strint=StringSubstr(str,1);
         long n=StringToInteger(strint);
         n++;
         ObjectSetString(0,name,OBJPROP_TEXT,"*"+(string)n);
         ObjectSetInteger(0,name,OBJPROP_TIME,1,time-hsize*n);
        }
     }
   ChartRedraw();
  }

Die Funktion besteht aus zwei ähnlichen Blocks: getrennt für Bid und Ask. Deshalb sind die Kommentare nur im ersten Block.

Beim aufmerksamen Leser ist sicher eine sinnvolle Frage entstanden: warum soll man den Preis im Namen des Objektes verdoppeln, wenn der sowieso in der Eigenschaft OBJPROP_PRICE erreichbar ist? Woran liegt der Sinn dieser Arbeit?

Diesen Punkt betrachten wir besser. Beim Ankommen des neuen Preises muss man bestimmen, in welcher Kolonne er sein wird, und dort entsprechend den Wert der Frequenz zu vergrössern. Wenn die Koordinate des Preises unmittelbar aus seiner Eigenschaft verwendet würde, so sollte man alle graphischen Objekte sortieren, den Wert dieser Eigenschaft anzufordern, mit dem kommenden Preis zu vergleichen und erst danach, den neuen Wert der Frequenz in die nötige Kolonne zu schreiben. Und die gültigen Zahlen der Art double zu vergleichen - ist es wieder ein "Hinterhalt". Im Allgemeinen ist es ein gar keiner effektiver Algorithmus. Andere Sache, wenn wir beim Ankommen die veränderten Frequenz des neuen Wertes des Preises dorthin schreiben, wohin es notwendig ist. Auf welche Weise? Es handelt sich darum, dass bei der Erstellung eines neuen graphischen Objektes mit einem gleichen Namen, mit dem einer schon existiert, wird der neue Objekt nicht erstellt und die Felder der Eigenschaften werden nicht auf 0 zurückgesetzt. Mit den anderen Worten, wir erstellen die Objekte, dabei nicht zu denken, dass sie verdoppelt werden. Man muss keine zusätzlichen Tests machen, weil Kopien und Klone nicht entstehen werden. Dafür wird das Funktional des Terminales und der graphischen Objekte sorgen. Man muss jetzt nur bestimmen, ob dieser Preis in der Auswahl das erste Mal oder nicht geraten ist. Dazu wird in der betrachteten Funktion DrawHistogram( die Präfix "das Sternchen" (*) in der Eigenschaft des Objektes OBJPROP_TEXT verwendet. Wenn es kein "Sternchen" gibt, so ist es ein solcher Preis zum ersten Mal gekommen. Wirklich, wenn wir das Objekt leer erstellen, ist dieses Feld auch leer. Bei den nachfolgenden Aufrufen wird dort der Wert der Frequenz mit dem gegebenen Präfix gespeichert.

Im folgenden Schritt machen wir die Absetzung des Histogramms nach rechts. Bei der Entstehung der neuen Bar wird der Graphik nach links geschoben, es sollte aber, dass das Histogramm auf der laufenden Bar immer dargestellt würde, d.h. es muss zur entgegengesetzten Seite geschoben werden. Schauen wir mal, wie es gemacht werden kann:

//--- Die Verschiebung der Diagramme auf die neue Bar 
   if(time[0]>prevTimeBar) // Bestimmen wir die Entstehung einer neuen Bar
     {
      prevTimeBar=time[0];
      // machen wir die Sortierung aller graphischen Objekte
      for(int obj=ObjectsTotal(0,-1,-1)-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,-1,-1);               // bekommen wir den Namen des gefundenen Objektes
         if(obj_name[0]==R)                                     // Suchen wir die Präfix des Elementes des Histogramms
           {                                                    // wenn wir das Element des Histogramms gefunden haben
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // Wir setzen den neuen Wert der Koordinate ein
                             0,time[0]);                        // für "0" ist es der Ankerpunkt
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);// Lesen wir die Variable aus der Eigenschaft des Objektes
            string strint=StringSubstr(str,1);                  // Wir wählen die Unterzeile aus, die aus der Variabel bekommen wurde
            long n=StringToInteger(strint);                     // wir umwandeln die Zeile in die Variable long
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // Wir berechnen den neuen Wert der Koordinate
                             1,time[0]+hsize*n);                // für "1" ist es der Ankerpunkt
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,
                             color_R_passive);                  // Wir ändern die Farbe des geschobenen Elementes des Histogramms
           }
         if(obj_name[0]==L)
           {
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,0,time[0]);
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);
            string strint=StringSubstr(str,1);
            long n=StringToInteger(strint);
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,1,time[0]-hsize*n);
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,color_L_passive);
           }
        }
      ChartRedraw();
     }

Eigentlich ist es Schluss. Das Histogramm der Preise Bid und Ask wird auf der Graphik gebaut, und das Programm verwendet kein Array oder einen Indikator-Puffer. Vollständig befindet sich der Code dieser Lösung im Abhang zum Artikel.


Auf Video wird die fertige Lösung der ähnlichen Aufgabe unter Verwendung des graphischen Gedächtnisses vorgeführt. Den Code kann man in CodeBase finden: der Indikator "Histogram bid and ask prices".

Die Beispiele der Konstruktion der Histogramme im Hauptfenster

Die Programmierung-Technologie der Funktional unter Verwendung des graphischen Gedächtnisses wurde betrachtet, jetzt bauen wir einige Histogramme für die standardmäßigen Indikatoren. Das Prinzip des Aufbaus ist ähnlich, nur werden wir statt der Preise Bid und Ask die Werte der Indikatoren auf der laufenden Bar verwenden.

     1. Der Indikator iMA. Wir nehmen zwei Indikatoren mit verschiedenen Perioden der Mittlung und bauen die Histogramme. So sieht der Code aus:

input int               period_MA1=20;       // Averaging period of iMA1 
input int               period_MA2=14;       // Averaging period of iMA2 
//---- indicator buffers
double      MA1[];
double      MA2[];
//---- handles for indicators
int         iMA1_handle;
int         iMA2_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iMA1_handle=iMA(_Symbol,_Period,period_MA1,0,MODE_SMA,PRICE_CLOSE);
   iMA2_handle=iMA(_Symbol,_Period,period_MA2,0,MODE_SMA,PRICE_CLOSE);
   ArraySetAsSeries(MA1,true);
   ArraySetAsSeries(MA2,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iMA1_handle,0,0,1,MA1);
   CopyBuffer(iMA2_handle,0,0,1,MA2);

   DrawHistogram(true,"iMA("+(string)period_MA1+")=",MA1[0],time[0],_Digits);
   DrawHistogram(false,"iMA("+(string)period_MA2+")=",MA2[0],time[0],_Digits);

//--- Die Verschiebung der Diagramme auf die neue Bar 
   if(time[0]>prevTimeBar) // Bestimmen wir die Entstehung einer neuen Bar
     {
      prevTimeBar=time[0];
      // machen wir die Sortierung aller graphischen Objekte
      for(int obj=ObjectsTotal(0,-1,-1)-1;obj>=0;obj--)
        {
         string obj_name=ObjectName(0,obj,-1,-1);               // bekommen wir den Namen des gefundenen Objektes
         if(obj_name[0]==R)                                     // Suchen wir die Präfix des Elementes des Histogramms
           {                                                    // wenn wir das Element des Histogramms gefunden haben
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // Wir setzen den neuen Wert der Koordinate ein
                             0,time[0]);                        // für "0" ist es der Ankerpunkt
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);// Lesen wir die Variable aus der Eigenschaft des Objektes
            string strint=StringSubstr(str,1);                  // Wir wählen die Unterzeile aus, die aus der Variabel bekommen wurde
            long n=StringToInteger(strint);                     // wir umwandeln die Zeile in die Variable long
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,           // Wir berechnen den neuen Wert der Koordinate
                             1,time[0]+hsize*n);                // für "1" ist es der Ankerpunkt
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,
                             color_R_passive);                  // Wir ändern die Farbe des geschobenen Elementes des Histogramms
           }
         if(obj_name[0]==L)
           {
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,0,time[0]);
            string str=ObjectGetString(0,obj_name,OBJPROP_TEXT);
            string strint=StringSubstr(str,1);
            long n=StringToInteger(strint);
            ObjectSetInteger(0,obj_name,OBJPROP_TIME,1,time[0]-hsize*n);
            ObjectSetInteger(0,obj_name,OBJPROP_COLOR,color_L_passive);
           }
        }
      ChartRedraw();
     }
   return(rates_total);
  }

in Abb. 2. Das Histogramm von 2 Indikatoren iMA.

in Abb. 2. Das Histogramm von 2 Indikatoren iMA. 

     2. Der Indikator iBands. Für diesen Indikator bauen wir die Histogramme für die oberen Grenze (UPPER_BAND) und unteren (LOWER_BAND). Den abgekürzten Code sieht man unten.

input int   period_Bands=14;           // Averaging period of iBands 
//---- indicator buffers
double      UPPER[];
double      LOWER[];
//---- handles for indicators
int         iBands_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iBands_handle=iBands(_Symbol,_Period,period_Bands,0,5.00,PRICE_CLOSE);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iBands_handle,1,0,1,UPPER);
   CopyBuffer(iBands_handle,2,0,1,LOWER);

   DrawHistogram(true,"iBands(UPPER)=",UPPER[0],time[0],_Digits);
   DrawHistogram(false,"iBands(LOWER)=",LOWER[0],time[0],_Digits);

//--- Die Verschiebung der Diagramme auf die neue Bar
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

in Abb. 3. Das Histogramm der Streifen Bollinger des Indikators iBands.

in Abb. 3. Das Histogramm der Streifen Bollinger des Indikators iBands. 

Die interessanten Histogramme entstehen für den Indikator Bollinger, wenn alle 3 Indikator-Puffer verwendet werden (0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND). Das Histogramm der oberen und unteren Streifen werden wir rechts von der laufenden Bar setzen, und das Histogramm der grundlegenden Linie — links von der nullwertigen Bar. Das Ergebnis ist auf der folgenden Bild vorgeführt:

in Abb. 4. Das Histogramm der Streifen Bollinger auf 3 Indikator-Puffern.

in Abb. 4. Das Histogramm der Streifen Bollinger auf 3 Indikator-Puffern.  

Den Code für diese Variante der Histogramme wird unten aufgeführt. Bitte Beachten Sie, dass tatsächlich zwei Histogramme rechts dargestellt werden: für die obere und untere Grenze des Indikators. Dabei werden sie optisch wie ein einziges Diagramm wahrgenommen.

input int   period_Bands=20;       // Averaging period of iBands 
//---- indicator buffers
double      BASE[];
double      UPPER[];
double      LOWER[];
//---- handles for indicators
int         iBands_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iBands_handle=iBands(_Symbol,_Period,period_Bands,0,10.00,PRICE_CLOSE);
   ArraySetAsSeries(BASE,true);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iBands_handle,0,0,1,BASE);
   CopyBuffer(iBands_handle,1,0,1,UPPER);
   CopyBuffer(iBands_handle,2,0,1,LOWER);

   DrawHistogram(true,"iBands(UPPER)=",UPPER[0],time[0],_Digits);
   DrawHistogram(true,"iBands(LOWER)=",LOWER[0],time[0],_Digits);
   DrawHistogram(false,"iBands(LOWER)=",BASE[0],time[0],_Digits);

//--- Die Verschiebung der Diagramme auf die neue Bar
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

Es waren bis jetzt die Beispiele der Variationsreihen betrachtet, in den das Intervall der Werte der untersuchten Größe (Varianten) der Größe des Punktes des laufenden Instruments _Point gleich war. Anders gesagt, alle Werte des Preises wurden von der separaten Kolonne des Histogramms gezeichnet.

Es ist nötig, zu betonen, wie fein und unmerklich für den Leser die Variationsreihen vom eingebauten Funktional des Terminales sortiert wurden. Dafür war es nicht nötig, mal eine Zeile des Codes zu schreiben.

Und jetzt schauen wir, wie das Intervall der Verteilung geändert werden kann, in dem einige Werte der Varianten sein werden, mit Hilfe nur einer Variable und verwendend des Funktionales der graphischen Objekte. In der betrachteten höher Funktion DrawHistogram() gibt es einen Eingangsparameter span — span des untersuchten Parameters. So abwechselnd im span können wir das Intervall für die Frequenzen-Rechnung des Histogramms ändern. Da im Name des graphischen Objektes den Preis gibt, so wird die Auswahl automatisch auf der Erstellung-Stufe und der Eigenschaften-Korrektur der Objekte durchgeführt. Schauen Sie mal, wie die Aufgaben der Sortierung und der Gruppierung unter Verwendung des graphischen Gedächtnisses ohne Programmieren leicht gelöst werden.

in Abb. 5. Das Histogramm der Preise Bid und Ask mit dem vergrösserten Intervall der kleinen Kolonne.

in Abb. 5. Das Histogramm der Preise Bid und Ask mit dem vergrösserten Intervall der kleinen Kolonne. 

Also, Der Aufbau des Histogramms im Hauptfenster des Charts ist sehr einfach gewesen. Jetzt stellen wir klar, wie die ähnlichen Diagramme in den zusätzlichen Fenstern des Charts zu machen.

Die Histogramme in den zusätzlichen Fenstern

Das Prinzip der Erstellung der statistischen Verteilungen in den zusätzlichen Fenstern ist genauso, wie auch im Hauptfenster. Der Unterschied liegt darin, dass die Werte der untersuchten Größen die negativen Größen und eine willkürliche span haben können. So hat die Preisskala im Hauptfenster die voraussichtlich bekannte span, die man mit Hilfe der Variable _Digits bekommen kann. In den zusätzlichen Fenstern können die Variablen sowie positive, als auch die negativen Werte übernehmen. Außerdem kann die Anzahl der bedeutenden Ziffern nach dem Komma beliebig sein. Deshalb muss man diese Besonderheit bei der Eingabe der Eingangsparameter der Funktion DrawHistogram() für den Aufbau der Histogramme berücksichtigen, sowie, die Nummer des zusätzlichen Fensters eingeben. Standartmäßig wird in der Funktion das Hauptfenster verwendet, welches dem 0 gleich ist

Die Beispiele der Konstruktion der Histogramme in den zusätzlichen Fenstern

Am besten werden die Informationen in den Beispielen begriffen. Mit ihnen werden wir uns eben beschäftigen. Betrachten wir einige Beispiele des Aufbaus der Histogramme in den zusätzlichen Fenstern für die standardmäßigen technischen Indikatoren.

     1. Der Indikator iChaikin.

in Abb. 6. Das Histogramm des Indikators Chaikin Oscillator.

in Abb. 6. Das Histogramm des Indikators Chaikin Oscillator.

Der abgekürzte Code für diesen Indikator:

input int   period_fast=3;       // Averaging period fast of iChaikin 
input int   period_slow=10;      // Averaging period of slow iChaikin 
//---- indicator buffers
double      CHO[];
//---- handles for indicators
int         iChaikin_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iChaikin_handle=iChaikin(_Symbol,_Period,period_fast,period_slow,MODE_SMA,VOLUME_TICK);
   ArraySetAsSeries(CHO,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iChaikin_handle,0,0,2,CHO);

   DrawHistogram(true,"iChaikin=",CHO[0],time[0],0,1);
   
//--- Die Verschiebung der Diagramme auf die neue Bar
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

     2. Der Indikator iCCI.

in Abb. 7. Das Histogramm des Indikators Commodity Channel Index.

in Abb. 7. Das Histogramm des Indikators Commodity Channel Index.

Der abgekürzte Code:

input int   period_CCI=14;             // Averaging period of iCCI 
//---- indicator buffers
double      CCI[];
//---- handles for indicators
int         iCCI_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iCCI_handle=iCCI(_Symbol,_Period,period_CCI,PRICE_CLOSE);
   ArraySetAsSeries(CCI,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iCCI_handle,0,0,2,CCI);

   DrawHistogram(true,"iCCI=",CCI[0],time[0],0,1);
   
//--- Die Verschiebung der Diagramme auf die neue Bar
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

     3. Die Indikatoren: iEnvelopesiATR, iMACD. Die Kombination der Histogramme für drei Indikatoren, jeder von denen im Fenster dargestellt werden.

in Abb. 8 dargestellt ist. Die Histogramme die 3 Indikatoren: iEnvelopes, iATR und iMACD.

in Abb. 8 dargestellt ist. Die Histogramme die 3 Indikatoren: iEnvelopes, iATR und iMACD. 

Der abgekürzte Code, der den Satz von Histogrammen realisiert, der in Abb. 8 dargestellt ist.

input int   period_Envelopes=14;       // Averaging period of iEnvelopes 
input int   period_ATR=14;             // Averaging period of iATR 
input int   period_fast=12;            // Averaging period fast of iMACD 
input int   period_slow=26;            // Averaging period of slow iMACD 
//---- indicator buffers
double      UPPER[];
double      LOWER[];
double      ATR[];
double      MAIN[];
double      SIGNAL[];
//---- handles for indicators
int         iEnvelopes_handle;
int         iATR_handle;
int         iMACD_handle;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   ObjectsDeleteAll(0,-1,-1);
   ChartRedraw();
   iEnvelopes_handle=iEnvelopes(_Symbol,_Period,period_Envelopes,0,MODE_SMA,PRICE_CLOSE,0.1);
   iATR_handle=iATR(_Symbol,_Period,period_ATR);
   iMACD_handle=iMACD(_Symbol,_Period,period_fast,period_slow,9,PRICE_CLOSE);
   ArraySetAsSeries(UPPER,true);
   ArraySetAsSeries(LOWER,true);
   ArraySetAsSeries(ATR,true);
   ArraySetAsSeries(MAIN,true);
   ArraySetAsSeries(SIGNAL,true);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   ArraySetAsSeries(time,true);
   
   CopyBuffer(iEnvelopes_handle,0,0,1,UPPER);
   CopyBuffer(iEnvelopes_handle,1,0,1,LOWER);
   CopyBuffer(iATR_handle,0,0,1,ATR);
   CopyBuffer(iMACD_handle,0,0,1,MAIN);
   CopyBuffer(iMACD_handle,1,0,1,SIGNAL);

   DrawHistogram(true,"iEnvelopes.UPPER=",UPPER[0],time[0],_Digits);
   DrawHistogram(false,"iEnvelopes.LOWER=",LOWER[0],time[0],_Digits);
   DrawHistogram(true,"iATR=",ATR[0],time[0],_Digits+1,1);
   DrawHistogram(true,"iMACD.SIGNAL=",SIGNAL[0],time[0],_Digits+1,2);
   DrawHistogram(false,"iMACD.MAIN=",MAIN[0],time[0],_Digits+1,2);

//--- Die Verschiebung der Diagramme auf die neue Bar
   if(time[0]>prevTimeBar)
     {
     ...
     }
   return(rates_total);
  }

Also, wenn wir schon die Aufbau-Technologie verschiedener Histogramme der statistischen Verteilungen der Charakteristiken des Marktes betrachtet haben, kann man behaupten, dass:

  • das Funktional des Handelsterminals МТ5;
  • die Möglichkeiten der graphischen Objekte MQL5;
  • Das graphische Gedächtnis (die wenig bekannten Möglichkeiten der graphischen Objekte);
  • die Elementarfunktion DrawHistogram()

ermöglichen, die Speicherhistogramme einer Menge der Indikatoren zu visualisieren und zu berechnen — von standardmäßigen bis zu benutzerdefinierten. Übrigens, für die ausführlichere Analyse der Marktcharakteristiken ist es ungenügend.

Die numerischen Charakteristiken der statistischen Verteilungen

Im ersten Teil des Artikels war die Aufbauweise der Histogramme für die Programmierer-Anfänger vorgeführt. Die aufgeführten Beispiele werden leicht auf die große Klasse der Aufgaben projiziert, die mit der Forschung der statistischen Verteilungen und von ihrer Visualisierung verbunden sind. Aber für die tiefere Analyse der Histogramme sind die universellsten und progressiven Methoden der Programmierung, und zwar — ООП notwendig. Also, betrachten wir, welche Parameter der statistischen Verteilungen für Leser interessant sind.

Zuallererst, uns interessieren die numerischen Charakteristiken der Variationsreihen:

  • Mittler arithmetisch (die mittlere Frequenz);
  • Der abgewogene arithmetische Mittelwert;
  • Dispersion;
  • Standardabweichung.

Zweitens ist die Visualisierung der gegebenen Charakteristiken notwendig. Natürlich, das alles kann man auch nach gewöhnlicher Weise realisieren, aber im vorliegenden Artikel werden wir die gestellten Aufgaben unter Verwendung der objekt-orientierten Programmierung lösen.

Die Klasse CHistogram

Das Funktional der gegebenen Klasse ermöglicht auf der Graphik des Histogramms und der Hauptcharakteristiken der statistischen Verteilungen darzustellen. Wir werden die Hauptmethoden betrachten.

Die Methode: der Konstrukteur der Klasse CHistogram.

Das initialisiert das Exemplar der Klasse. 

void CHistogram(
   string name,                     // die einzigartige Präfix des Namens
   int    hsize,                    // Maßstab des Diagramms
   int    width,                    // Die Dicke der Kolonnen-Linien des Histogramms
   color  active,                   // Die Farbe der aktiven Linien
   color  passive,                  // Die Farbe der passiven Linien
   bool   Left_Right=true,          // left=false or right=true
   bool   relative_frequency=false, // Das relative oder absolute Histogramm
   int    sub_win=0                 // Der Index des Aufbau-Fensters des Histogramms
   );

Parameter:

 name

    [in] die einzigartige Präfix des Namens für die allen Kolonnen des Histogramms. 

 hsize

    [in] Der Maßstab der Darstellung des Histogramms

 width

    [in] Die Dicke der Kolonnen-Linien des Histogramms.

 active

    [in] Die Farbe der Kolonnen des Histogramms, die auf der laufenden Bar erneuert wurden.

 passive

    [in] Die Farbe der Kolonnen des Histogramms, die auf der laufenden Bar nicht erneuert wurden.

 Left_Right=true

    [in] Die Richtung der Darstellung des Histogramms. false — das Histogramm ist links von der laufenden Bar, true — rechts.

 relative_frequency=false

    [in] Die Methode der Berücksichtigung der Werte der Frequenzen. false — die absoluten Werte der Frequenzen, true — die relativen Werte der Frequenzen.

 sub_win=0

    [in] Der Index des Aufbau-Fensters des Histogramms. 0 — das Hauptfenster des Charts. 

Rückgabewert:

 Es gibt keinen Rückgabewert. Falls es erfolgreich wird, wird ein Exemplar der Klasse mit den gegebenen Parametern erstellt.

 

Die Methode: die Darstellung des Histogramms DrawHistogram.

Es stellt die Kolonnen des Histogramms dar: erstellt neue, editiert die vorhandenen, speichert die Werte der Frequenzen im graphischen Gedächtnis. Es stellt das Histogramm auf der laufenden Bar dar.

void DrawHistogram(
   double price,  // Die Werte die Varianten
   datetime time  // Die Zeit der laufenden Bar
   );

Parameter:

 price

    [in] die Werte die Varianten der untersuchten Charakteristik des Marktes.

 time

    [in] Die Zeit der laufenden Bar. Auf dieser Bar wird die Achse des Histogramms sein.  

Rückgabewert:

 Es gibt keinen Rückgabewert. Falls es erfolgreich wird, wird eine neue oder korrigierte Kolonne des Histogramms erstellt. Wenn eine neue Bar entsteht, wird das Histogramm so geschoben, damit die Achse sich auf der laufenden Bar befindet. 

 

die Methode: die Berechnung der Charakteristiken des Histogramms HistogramCharacteristics. 

Es gibt die berechneten Charakteristiken der Variationsreihe in die Variabel als sVseries zurück.

sVseries HistogramCharacteristics();

Parameter:

 Es gibt keine Eingangsparameter.

Rückgabewert:

 Falls es erfolgreich wird, gibt er den Wert der Variable des Typs sVseries zurück.

 

Die Struktur für das Erhalten der laufenden Werte der Charakteristiken des Histogramms (sVseries).

Die Struktur für die Speicherung der letzten Werte der Charakteristiken der statistischen Verteilung. Es ist für das Erhalten der am meisten geforderten Informationen über die Variationsreihe vorgesehen. 

struct sVseries
  {
   long     N;    // Die Gesamtzahl der Beobachtungen
   double   Na;   // Der Mittelwert der Frequenzen
   double   Vmax; // Der Maximalwert der Varianten
   double   Vmin; // Der Minimalwert der Varianten
   double   A;    // Die Amplitude der Reihe
   double   Mean; // Der abgewogene arithmetische Mittelwert
   double   D;    // Dispersion
   double   SD;   // Standardabweichung
  };

Die Variable des Typs sVseries ermöglicht über einen Aufruf der Funktion HistogramCharacteristics() die Werte aller Hauptcharakteristiken der Variationsreihe zu bekommen, die in Form vom Histogramm dargestellt wurde.

 

Die Methode: die Visualisierung des Mittelwertes DrawMean. 

Es stellt den abgewogen arithmetischen Mittelwert der Variationsreihe auf dem Chart dar. 

void DrawMean(
   double coord,     // Der abgewogene arithmetische Mittelwert
   datetime time,    // Die Zeit der laufenden Bar
   bool marker=false,// Soll der Marker dargestellt werden oder nicht
   bool save=false   // Soll der Wert in der History gespeichert werden oder nicht
   );

Parameter:

 coord

    [in] Der abgewogene arithmetische Mittelwert.

 time

    [in] Die Zeit der laufenden Bar. Auf dieser Bar wird der abgewogene arithmetische Mittelwert sein.

 marker=false

    [in] Soll der Marker am Chart dargestellt werden oder nicht. false — der Marker ist nicht dargestellt, true — der Marker ist am Chart dargestellt.

 save=false

    [in]  Die Speicherung des abgewogenen arithmetischen Mittelwertes in der History. false — der Marker wird nicht dargestellt, true — der Marker wird am Chart dargestellt

Rückgabewert:

Falls es erfolgreich wird, wird die horizontale Linie dargestellt, die dem Wert des abgewogenen arithmetischen Mittelwert entspricht. 

 

Die Methode: die Visualisierung der Standardabweichung DrawSD.

Es stellt den Wert der Standardabweichung in Form vom Rechteck dar, dessen Breite mit der mittleren Frequenz übereinstimmt, und die Höhe ist der Standardabweichung gleich ist, die nach oben und nach unten von dem abgewogenen arithmetischen Mittelwert verschoben ist.

void DrawSD(
   sVseries &coord,        // die Variable des Typs sVseries
   datetime time,          // Die Zeit der laufenden Bar
   double deviation=1.0,   // Die Abweichung
   color clr=clrYellow     // die Farbe der Abweichung
   );

Parameter:

 coord

    [in] Der Wert der Variable des Typs sVseries.

 time

    [in] Die Zeit der laufenden Bar.

 deviation=1.0

    [in] Der Koeffizient, auf dem der Wert der Standardabweichung vergrössert sein wird.

 clr=clrYellow

    [in] die Farbe des Rechteckes, welches die Standardabweichung visualisiert.

Rückgabewert

 Falls es erfolgreich wird, wird das Rechteck am Chart dargestellt, welches die Standartabweichung vom abgewogenen arithmetischen Mittelwert charakterisiert.

  

Das Beispiel des Aufbaus des Histogramms unter Verwendung der Klasse CHistogram

Als Beispiel werden wir die Histogramme der statistischen Verteilungen der Preise Bid und Ask, sowie zwei Indikatoren ATR und MACD  bauen(in Abb. 9). Im Unterschied zu den vorhergehenden Beispielen, mit Hilfe der betrachteten Klasse kann man die relativen Diagramme erstellen. Für die Preise, außer dem Histogramm, werden WIR die graphische Darstellung des abgewogen arithmetischen Mittelwertes und der Standartabweichung erstellen. Dabei werden wir den Mittelwert mit Hilfe der Indikator-Puffer speichern. Auf dem Bild wurden die Standartabweichungen in Form von den Rechtecken dargestellt, deren Breite der mittleren Frequenz entspricht, und die Höhe ist zwei Abweichungen gleich, die für die Anschaulichkeit mehrmals vergrössert sind. 

 in Abb. 9. Die Histogramme von 2 Indikatoren: iATR, iMACD und den Preisen Bid und Ask.

in Abb. 9. Die Histogramme von 2 Indikatoren: iATR, iMACD und den Preisen Bid und Ask.


Der Code dieses Beispiels befindet sich im Anhang zum Artikel. Die Verwendung der Indikator-Puffer im vorliegenden Programm wurden dargestellt, um die Verbindung zwischen der betrachteten Klasse und dem Funktional des Indikators zu demonstrieren.

Fazit

  • Die statistischen Verteilung der Variationsreihen in Form von den Histogrammen, ohne Verwendung der Arrays und der Indikator-Puffer kann man leicht unter Verwendung "des graphischen" Gedächtnisses realisieren.
  • In jenen Aufgaben, wo die graphischen Konstruktionen (die Objekte) verwendet werden, in den überwiegenden Fällen ist die Verwendung der Eigenschaften der graphischen Objekte vernünftig, um den Speicherplatz zu sparen. Dabei bekommen wir den Zugriff zu den umfangreichen Möglichkeiten des Funktionales des Terminals und der Sprache MQL5: von solchem, wie die Sortierung, die Gruppierung, die Suche, die Auswahl, der direkte Zugriff zu den Elementen und anderem.
  • Es wurden die grundlegenden, primitiven Weisen der Verwendung "des graphischen" Gedächtnisses betrachtet. In der Tat kann man die kleinen Arrays und die Strukturen in den Eigenschaften der graphischen Objekte speichern.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/2714

LifeHack für Trader: Der vergleichende Bericht über einige Tests LifeHack für Trader: Der vergleichende Bericht über einige Tests
Im Artikel wird der Test des EAs betrachtet, der zugleich auf 4 verschiedenen Symbolen gestartet wird. Der endgültige Vergleich der 4 Testberichte wird in einer Tabelle aufgeführt, genauso wie bei einer Auswahl der Waren in einem Internet-Geschäft. Als zusätzlicher Bonus kommen dazu die automatisch erstellten Grafiken der Verteilung für jedes Symbol.
Die Trading-Strategie '80-20' Die Trading-Strategie '80-20'
Im Artikel wird die Erstellung der Instrumente (des Indikators und des EAs) für die Forschung der Handelsstrategie '80-20' beschrieben. Die Regeln der TS sind aus dem Buch Lindy Raschke und Lawrance Konnorsa "Street Smarts High Probability Short-Term Trading Strategies" genommen. In der Sprache MQL5 wurden die Regeln dieser Strategie formalisiert, und die auf ihrer Grundlage erstellten Indikatoren und der EA auf der aktuellen History des Marktes geprüft.
Ein Beispiel für die Entwicklung einer Spread-Strategie basierend auf Futures der Moskauer Börse Ein Beispiel für die Entwicklung einer Spread-Strategie basierend auf Futures der Moskauer Börse
MetaTrader 5 erlaubt es, Roboter zu entwickeln und zu testen, die gleichzeitig auf mehreren Symbolen handeln. Der in die Plattform integrierte Strategietester lädt die Tickshistorie vom Server des Brokers automatisch herunter und berücksichtigt Kontraktspezifikationen: der Entwickler muss nichts manuell machen. Dies lässt einfach und präzise alle Bedingungen der MetaTrader 5 Handelsumgebung programmieren und Roboter testen, mit den Intervallen von bis zu Millisekunden zwischen dem Eingehen von Ticks auf verschiedenen Symbolen. In diesem Artikel zeigen wir, wie eine Spread-Strategie basierend auf zwei Futures der Moskauer Börse entwickelt und getestet werden kann.
Universeller ZigZag Universeller ZigZag
ZigZag ist einer der beliebtesten Indikatoren unter MetaTrader 5 Nutzern. Im Artikel werden die Möglichkeiten der Erstellung verschiedener Varianten des ZigZag Indikators analysiert. Als Ergebnis bekommen wir einen universellen Indikator mit breiten Möglichkeiten für die Erweiterung der Funktionalität, den man bei der Entwicklung neuer Expert Advisors und anderer Indikatoren verwenden kann.