English Русский 中文 Español 日本語 Português
Informationsspeicherung und Informations-Darstellung

Informationsspeicherung und Informations-Darstellung

MetaTrader 4Handel | 23 Dezember 2015, 10:46
1 195 0
Andrey Khatimlianskii
Andrey Khatimlianskii

1. Einführung

Haben Sie noch nie ein paar Stunden Zeit auf der Suche nach einer wichtigen Information verloren, die vorsorglich von Ihrem Experten in einer Log-Datei eingetragen wurde? Oder vielleicht waren Sie schon mal müde von kleinen einfarbigen Buchstaben, die im Bildschirm von der Funktion Comment() angezeigt wurden? Allerdings können sie oft sehr wertvoll für das Handeln sein. Wenn es Ihnen schon bekannt vorkommt, worum es geht, dann ist der Artikel genau für Sie.

Die wichtigsten Aufgaben, welche mir während der Arbeit an diesem Artikel eingefallen sind:

  • Erstellung einer Funktion, die dem Experten die Information zur Eintragung in seiner persönlichen Log-Datei ermöglicht(ähnlich mit der Funktion Print());
  • Erstellung eines Werkzeugs, das eine visuell bequemere Formatierung der Information ermöglicht, welche im Bildschirm mit geänderten Farben und Schriftgrößen angezeigt wird.

2. Die persönliche Log-Datei des Experten

Wie ich bereits erwähnt habe, ist die Funktion Print() nicht immer bequem, um die Informationen, die der Experte erzeugt, während der Arbeit einzutragen. Es fällt insbesondere auf, wenn ein paar Experten auf dem Terminal zugleich handeln. Jeder Experte trägt nur seine Informationen in der Log-Datei ein, und dann ist es kompliziert da etwas zu finden, geschweige denn eine aufbauende Analyse zu dieser Information - das ist ein sehr komplizierter und anstrengender Prozess.

Das Problem kann man ziemlich einfach lösen. Jeder Experte muss einfach seine persönliche Log-Datei erstellen. Und statt die Information in einer allgemeinen Log-Datei einzutragen, muss es in seiner persönlichen Log-Datei eingetragen werden. Um diese Möglichkeit mit Leichtigkeit zu nutzen, Lassen Sie uns direkt einen Code als Funktion schreiben.

Also, was muss diese Funktion machen können:

  • Sie muss eine Datei mit einem einzigartigen Namen erstellen können;
  • Sie muss nach Bedarf in dieser Datei Informationen eintragen können;
  • Sie muss die Datei nach der Arbeit des Experten schließen, damit andere Anwendungen Zugriff zu dieser Datei haben.

Alles ist einfach und eindeutig. Aber das einzige, was noch etwas strittig bleibt, ist die Vorschrift, dass die Datei nach jeder Eintragung geschlossen werden muss. Einerseits hätte es ermöglicht, diese Datei während der Arbeit des Experten in einer anderen Anwendung öffnen zu können. Aber anderseits kann die Information beschädigt werden, wenn der Experte die Datei für eine neue Eintragung nicht öffnen kann, und die Datei wird bereits von einer anderen Anwendung verwendet. Dabei kann man die Information einfach verlieren. Das darf man nicht erlauben, obwohl es ein Programm dafür gibt, das die Dateien nur den Zweck des Zuschauens öffnen kann, und dabei stört es nicht den MetaTrader, mit ihnen zu arbeiten.

Da eine Datei nur einmal geöffnet und geschlossen werden muss, werden wir den Code, der diese Aktion erledigt, in den Funktionen init() und deinit() platzieren. Damit er möglichst wenig Platzt einnimmt, erstellen wir ihn wie eine Funktion:

int log_handle = -1;
 
//+---------------------------------------------------------------+
// void log_open( string ExpertName = "Expert" )
//
// Die Funktion, die die persönliche Log-Datei des Experten öffnet.
// Das Verzeichnis, in dem die Datei erstellt wird:
// "...\MetaTrader 4\experts\files\logs\ExpertName\"
// Der Dateiname - das Eintragungsdatum in der Form "Jahr,Monat,Tag"
//+---------------------------------------------------------------+
void log_open( string ExpertName = "Expert" )
 {
     string log_name = "logs\\" + ExpertName + " (" + Symbol() + ", " + 
                    strPeriod( Period() ) + ")\\" + TimeToStr( LocalTime(),
                    TIME_DATE ) + ".txt";
  log_handle = FileOpen ( log_name, FILE_READ | FILE_WRITE, " " );
  if( log_handle < 0 )
     {
         int _GetLastError = GetLastError();
    Print( "FileOpen( ", log_name, 
          ", FILE_READ | FILE_WRITE, \" \" ) - Error #", 
          _GetLastError );
    return(-1);
   }
 }
string strPeriod( int intPeriod )
 {
     switch ( intPeriod )
     {
         case PERIOD_MN1: return("Monthly");
    case PERIOD_W1:  return("Weekly");
    case PERIOD_D1:  return("Daily");
    case PERIOD_H4:  return("H4");
    case PERIOD_H1:  return("H1");
    case PERIOD_M30: return("M30");
    case PERIOD_M15: return("M15");
    case PERIOD_M5:  return("M5");
    case PERIOD_M1:  return("M1");
    default:        return("UnknownPeriod");
   }
 }
 
//+---------------------------------------------------------------+
// log_close()
//
// Die Funktion, die die persönliche Log-Datei des Experten schließt.
//+---------------------------------------------------------------+
void log_close()
 {
      if( log_handle > 0 ) FileClose( log_handle );
 }

Jetzt, wenn wir schon eine geöffnete Datei haben, können wir in dieser Datei Informationen eintragen. Dafür

  • bewegen wir den Mauszeiger zum Dateiende, damit wir die Information nicht verlieren;
  • Fügen wir am Anfang der Zeile die Eintragungszeit ein. Das benutzen wir noch bei der Analyse;
  • Schreiben wir den Text in der Datei;
  • Speichern wir die vervollständigte Datei auf der Festplatte. Wenn der Experte wegen eines Notabsturzes seine Arbeit beendet, wird es Datenverlusten vorbeugen.

Schließlich müssen wir eine solche Funktion haben:

//+---------------------------------------------------------------+
// log( string text )
//
// Die Funktion, welche die Zeile text in der persönlichen Log-Datei des Experten schreibt.
//+------------------------------------------------------------------+
void log( string text )
 {
     int _GetLastError = 0;
   if( log_handle < 0 )
      {
             Print( "Log write error! Text: ", text );
     return(-1);
    }
    
     //---- Stellen wir den Dateizeiger am Ende der Datei
     if( !FileSeek ( log_handle, 0, SEEK_END ) )
     {
          _GetLastError = GetLastError();
    Print( "FileSeek ( " + log_handle + ", 0, SEEK_END ) - Error #", 
           _GetLastError );
    return(-1);
   }
 
    //---- Wenn die Zeile, die der Experte schreiben will, ist kein Symbol 
 //des Zeilenumbruchs, 
    //---- Fügen wir am Anfang der Zeile die Eintragungszeit ein 
    if( text != "\n" && text != "\r\n" )
         text = StringConcatenate( TimeToStr( LocalTime(), TIME_SECONDS ), 
                             " - - - ", text );
  if ( FileWrite ( log_handle, text ) < 0 )
     {
           _GetLastError = GetLastError();
    Print( "FileWrite ( ", log_handle, ", ", text, " ) - Error #", 
          _GetLastError );
    return(-1);
   }
    //---- Speichern wir den geschriebenen Text auf der Festplatte
    FileFlush( log_handle );
 }
Nun können wir sicher in allen Experten das Wort Print mit dem Wort log auswechseln, dabei aber nicht vergessen, die Funktionsaufrufe log_open und log_close hinzuzufügen.

Hier ist ein einfaches Beispiel vom Experten, der die enthaltende Datei log.mq4 verwendet:

#include <log.mq4>
 
int init()
  {
    log_open( "log_test" );
    log( "Die Lof-Datei ist erfolgreich geöffnet, der Experte hat die Arbeit begonnen..." );
    return(0);
  }
int deinit()
  {
    log( "Schließen wir die Log-Datei, der Experte beendet die Arbeit..." );
    log_close();
    return(0);
  }
int start()
  {
    log( "Neuer Tick: Bid = " + DoubleToStr( Bid, Digits ) );
    return(0);
  }


3. Die Informationslieferung auf den Bildschirm

Nun, wenn das Problem mit der Log-Datei schon gelöst ist, kann man anfangen, die Informationsdarstellung anzupassen, die auf den Bildschirm geliefert wird.


Erstmal betrachten wir alle möglichen Methoden, diese Aufgaben zu lösen. In der MQL4-Sprache wird die Informationen durch die Funktion Comment() angezeigt, aber wegen der oben beschriebenen Gründe passt diese Funktion nicht. Deshalb muss man eine andere Lösung finden. Beispielsweise können daraus Objekte werden, die einen Text enthalten. Die sind nur zwei - "Text" und "Text Label" (Text Markierung). Der grundlegende Unterschied zwischen ihnen besteht darin, dass der "Text" mit den Grafik-Koordinaten (Zeit und Preis) verbunden wird, und "Text Label" - wird mit den Koordinaten des Fensters verbunden. Von daher, dass wir die Information immer am rechten Ort brauchen, egal ob die Grafik verschoben wird oder der Bildausschnitt durch Zoomen verändert wird, werden wir "Text Label" verwenden.


Für die Erstellung und Kontrolle der Objekten in MQ4 gibt es ein paar Funktionen. Alle ihre Namen beginnen mit dem Wort Object. Schauen wir mal, welche von ihnen für uns nützlich werden können:

  • bool ObjectCreate(...) – für die Erstellung eines Objektes;
  • bool ObjectDelete(...) – für die Löschung eines Objektes, nach seiner Verwendung;
  • bool ObjectSet(...) – für die Änderung der Objekt-Eigenschaften, wie zum Beispiel den Ankerpunkt(x,y);
  • bool ObjectSetText(...) – für die Text-Lieferung;
  • void ObjectsRedraw() – um Objekten nach einer Textänderung neu zu zeichnen.

Also, was müssen wir jetzt tun:

  • In der Funktion init() des Experten ein Objekt für die Information-Lieferung erstellen;
  • In der Funktion deinit() des Experten alle erstellten Objekte löschen;
  • In der Funktion start() die Möglichkeit zu haben, den Text, die Farbe und die Schriftgröße bei allen erstellten Objekten zu ändern.
Wir haben wieder 3 Funktionen, die ihre eigene Aufgabe erledigen.

Vor dem Schreiben des Codes muss man eine unangenehme Einschränkung erwähnen, die bei der Verwendung des Objektes"Text Label" auftritt. Es kann nur eine Zeile enthalten, das heißt, es kann keine Symbole des Zeilenumbruchs enthalten. Und man weiß, dass Informationen viel einfacher wahrzunehmen sind, wenn sie sich über mehrere Zeilen erstrecken. Deshalb müssen wir ein paar Objekte erstellen und danach zwischen ihnen die Information verteilen. Ich habe 5 "Zeilen" geschrieben, aber Sie können diese Anzahl nach freiem Ermessen ändern.


Außerdem, gibt es eine Einschränkung für die Länge des gelieferten Textes. Deshalb habe ich noch eine zweite "Spalte" hinzugefügt, das heißt, noch 5 Zeilen rechts hinzugefügt.

So sieht die Funktion info_init() aus, welche die Objekte erstellt:

///////////////////////////////////////////////////////////////
// void info_init()
//
// Die Erstellung der Objekte, um die Information auf den Bildschirm zu liefern
//////////////////////////////////////////////////////////////////
void info_init()
  {
    for ( int row = 0; row <= 4; row ++ )
      {
        _LabelCreate(StringConcatenate( "InfoLabel_0", row ),   4, 
                     15 + 15*row );
        _LabelCreate(StringConcatenate( "InfoLabel_1", row ), 270,
                     15 + 15*row );
      }
  }
 
//////////////////////////////////////////////////////////////////
// void _LabelCreate ( string _Name, int _XDistance, int _YDistance, 
// int _Corner = 0 )
//
// Die Erstellung des Objektes "Text Label" mit dem Namen _Name.
// Koordinaten: х = _XDistance, у = _YDistance, Winkel = _Corner.
//////////////////////////////////////////////////////////////////
void _LabelCreate (string _Name, int _XDistance, int _YDistance, 
                   int _Corner = 0)
  {
    int _GetLastError;
 
    if( !ObjectCreate( _Name, OBJ_LABEL, 0, 0, 0 ) )
      {
        _GetLastError = GetLastError();
        if ( _GetLastError != 4200 )
          {
           Print("ObjectCreate( \"", _Name, "\", OBJ_LABEL,0,0,0 ) - Error #",
                 _GetLastError );
           return(-1);
          }
      }
    if( !ObjectSet( _Name, OBJPROP_CORNER, _Corner ) )
      {
        _GetLastError = GetLastError();
        Print("ObjectSet( \"", _Name, "\", OBJPROP_CORNER, ",
              _Corner, " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_XDISTANCE, _XDistance ) )
      {
        _GetLastError = GetLastError();
        Print("ObjectSet( \"", _Name, "\", OBJPROP_XDISTANCE, ", 
              _XDistance, " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_YDISTANCE, _YDistance ) )
      {
        _GetLastError = GetLastError();
        Print("ObjectSet( \"", _Name, "\", OBJPROP_YDISTANCE, ", 
              _YDistance, " ) - Error #", _GetLastError );
      }
    if( !ObjectSetText ( _Name, "", 10 ) )
      {
        _GetLastError = GetLastError();
        Print("ObjectSetText( \"", _Name, "\", \"\", 10 ) - Error #",
              _GetLastError );
      }
  }

Wie Sie sehen können, werden diese Objekte als "InfoLabel_" genannt, zusätzlich wird noch die Objektnummer (von 00 bis 04 - die linke "Spalte", und von 10 bis 14 - die rechte Spalte angefügt). Objekte werden in der linken oberen Ecke verankert. Oben wurde absichtlich einen Abstand gemacht, denn man hat sich schon daran gewöhnt da die OHLS-Informationen über das aktuelle Bar zu sehen. Den vertikalen Abstand zwischen den Zeilen habe ich in Höhe von 15 festgelegt, was genug ist für einen normalen Text. Also, die Initialisierung ist vorbei, Lassen Sie uns jetzt eine Deinitialisierung durchführen - sie werden sich sehr ähneln:

/////////////////////////////////////////////////////////////////////////////////
// void info_deinit()
//
// Löschung der Objekte, die von der Funktion info_init() erstellt wurden
/////////////////////////////////////////////////////////////////////////////////
void info_deinit()
 {
     int _GetLastError;
   for ( int row = 0; row <= 4; row ++ )
          {
             if ( !ObjectDelete( StringConcatenate( "InfoLabel_0", row ) ) )
           {
                   _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_0", row ), 
             "\" ) - Error #", _GetLastError );
      }
          if( !ObjectDelete( StringConcatenate( "InfoLabel_1", row ) ) )
           {
                   _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_1", row ), 
             "\" ) - Error #", _GetLastError );
      }
      }
 }


Und jetzt das Interessanteste - und zwar die Information-Lieferung.

Also, wir haben 10 Objekte, die an ihren Stellen bleiben und sie sind bereit, den Text für die Darstellung "anzunehmen". Lassen Sie uns überlegen, wie wir die Verwendung der Informationsanzeigefunktion möglichst einfach machen können.

Erstens, statt den Objektnamen anzugeben, wird es einfacher seine Nummer anzugeben.

Zweitens, es müssen nur 2 Parameter verwendet werden: die Objektnummer und die Nummer des gelieferten Textes. Die restlichen Parameter kann man wahlfrei lassen, da die Textfarbe oder die Schriftgröße nicht häufig geändert werden müssen. Hier sollte man nachdenken, was es soll , wenn Parameter übersprungen wurden. Es gibt zwei Varianten:

  • entweder Parameter standardmäßig zu verwenden;
  • oder die letzten verwendeten Parameter wieder zu verwenden.

Wie Mir scheint, ist die zweite Variante ist bequemer - Standardmäßige Werte sollte man sowieso im Code für jeden Experten ändern, und die letzten verwendeten Parameter werden automatisch bei jeder Funktion-Verwendung gespeichert.

Also, so sieht die Funktion aus:

void info( int LabelNumber, string Text, color Color = -1, 
              double FontSize = -1.0, string Font = "-1" )
 {
     //---- Bestimmen den Objektnamen
     string LabelName;
  if( LabelNumber < 10 )
          LabelName = StringConcatenate( "InfoLabel_0", LabelNumber );
  else
          LabelName = StringConcatenate( "InfoLabel_" , LabelNumber );
 
  //---- wenn die Werte der zusätzlichen Parameter nicht angegeben wurden, 
    //---- Setzen wir die letzten verwendeten Werte ein
    if( Color < 0 ) 
          Color = lastusedColor;
  if ( FontSize < 0 ) 
    FontSize = lastusedFontSize;
  if ( Font == "-1" ) 
    Font = lastusedFont;
 
  //---- Merken wir uns die letzten verwendeten Werte
     lastusedColor = Color;
  lastusedFontSize = FontSize;
  lastusedFont = Font;

Um die Situation zu vermeiden, in der lastused Variablen die nullwertigen Werte haben werden, Lassen Sie uns ihre Werte bei der Deklaration ordnen:

color lastusedColor = Black;
double lastusedFontSize = 9.0;
string lastusedFont = "Arial";


Es ist Ihnen vielleicht schon aufgefallen, dass lastused-Variablen außerhalb der Funktion deklariert wurden. Dadurch werden ihre Werte nicht bei jedem Aufruf der Funktion info() auf 0 zurückgesetzt.

Und jetzt, eigentlich zeigen wir den neuen Text und zeichnen Objekte neu. Das müssen wir tun, damit die Änderungen, die wir in Objekten hinzugefügt haben, direkt im Bildschirm angezeigt werden:

    //---- Zeigen wir den neuen Text an
    if ( !ObjectSetText( LabelName, Text, FontSize, Font, Color ) )
      {
        int _GetLastError = GetLastError();
        Print("ObjectSetText( \"", LabelName,"\", \"", Text, "\", ", 
              FontSize, ", ", Font, ", ", Color, " ) - Error #", 
              _GetLastError );
      }
    //---- Zeichnen wir Objekte neu
    ObjectsRedraw();
  }

Der ganze Code alle 3 Funktionen können Sie in der Anhang-Datei info.mq4 finden.

Und jetzt schauen wir mal, was wir haben:





Meiner Meinung nach ist es nicht schlecht.

Zuletzt kann man noch mehr Komfort durch eine Funktion durchsetzen - die die ganze Information löscht. Sie wird als info_clear() benannt, und wie sie verwendet werden soll, denke ich mal, wissen Sie selber.

void info_clear()
 {
     for ( int n = 0;  n < 5;  n ++ ) 
            info( n, "" );
   for (     n = 10; n < 15; n ++ ) 
     info( n, "" );
 }

4. Abschluss

Im Artikel wurden die alternativen Methoden beschrieben, um Log-Datei einzutragen und die Informationen auf den Bildschirm zu liefern. Auch wurden zwei enthaltene Dateien erstellt, die in den Ordner "experts/include", – log.mq4 und info.mq4 gespeichert werden müssen. Die können Sie von jedem Experten ausverwenden.

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

Beigefügte Dateien |
info.mq4 (9.18 KB)
info_test.mq4 (2.54 KB)
log.mq4 (6.65 KB)
log_test.mq4 (1.68 KB)
Die Synchronisation Experts,Skripts und Indikatoren Die Synchronisation Experts,Skripts und Indikatoren
Betrachtet wird die Notwendigkeit und allgemeine Grundlagen zum Aufbau des Programmkomplexes, der einen Experte, einen Skript und einen Indikator enthält.
Charts ohne "Gaps" (Lücken) Charts ohne "Gaps" (Lücken)
Der Artikel widmet sich der Realisierung der Charts ohne verpasste Bars.
Das Expertensystem "Kommentator". Die praktische Verwendung der eingebauten Indikatoren im MQL4-Programm Das Expertensystem "Kommentator". Die praktische Verwendung der eingebauten Indikatoren im MQL4-Programm
Der Artikel betrachtet die Verwendung der technischen Indikatoren beim Programmieren in der MQL4-Sprache.
Orders steuern ist einfach Orders steuern ist einfach
Der Artikel beschreibt die unterschiedlichen Arten der Kontrolle über den geöffneten Positionen und Pending Orders und soll bei der Erstellung eines Expertes helfen.