English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Wie erstelle ich MetaTrader 5-Angebote für andere Applikationen

Wie erstelle ich MetaTrader 5-Angebote für andere Applikationen

MetaTrader 5Beispiele | 5 Mai 2016, 13:47
662 0
Anatoli Kazharski
Anatoli Kazharski

Inhalt

Einleitung
1. Themenbereiche
2. Datenformat
3. Externe Programmparameter
4. Von Nutzern eingegebene Parameter überprüfen
5. Globale Variablen
6. Informationsfenster
7. Applikationshauptblock
8. Ordner anlegen und Daten ablegen
Fazit


Einleitung

Bevor ich damit begann, mich mit MQL5 vertraut zu machen, hatte ich mich bereits mit einigen anderen Apps zur Entwicklung von Trading-Systemen beschäftigt. Ich kann nicht behaupten, dass ich meine Zeit verschwendet habe. Einige von ihnen enthalten durchaus nützliche Tools, die Nutzern Zeit sparen, viele verschiedene Themengebiete abdecken, Mythen dekonstruieren und Entwicklungsimpulse geben, ohne dass es der Kenntnisse von Programmiersprachen bedarf.

Diese Apps bedürfen historischer Daten. Aufgrund des Fehlens gewisser Datenstandardformate mussten Sie allerdings oft vor ihrer Benutzung editiert werden (mittels Excel beispielsweise), damit sie dem zulässigen Format eines entsprechenden Programms entsprechen. Selbst wenn es Ihnen gelingt, alle notwendigen Details zu verstehen, so müssen Sie sich dennoch um viele wichtige Dinge selbst kümmern. Nutzer können sich verschiedener Skriptversionen bedienen, um die Angebote von MetaTrader 4 in das notwendige Format zu überführen. Falls es eine entsprechende Nachfrage geben sollte, so könnten auch wir eine MQL5-Skriptversion entwickeln.


1. Themenbereiche

Dieser Artikel befasst sich mit den folgenden Themenschwerpunkten:

  • mit der Symbolleiste des Market Watch-Fensters und der üblichen Server-Symbolleiste arbeiten
  • das Überprüfen der Datentiefe und das eventuell notwendige Herunterladen fehlender Datenmengen, um verschiedene Situationen sicher zu meistern
  • das Anzeigen von Informationen betreffend die Anforderung von Daten im Custom Panel-Chart und dem Journal
  • das Präparieren von Daten, um sie in einem nutzerfreundlichen Format abzulegen
  • das Anlegen von Verzeichnisstrukturen anlegen
  • das Ablegen von Daten


2. Datenformat

Ich werde ein Beispiel zur Präparierung von Daten präsentieren, die in NeuroShell DayTrader Professional (NSDT) verwendet werden sollen. Ich habe beide Versionen getestet - NSDT 5 und NSDT 6 - und fand heraus, dass beide unterschiedliche Datenformate bevorzugen. Die NSDT v.5-Daten und -Zeitdaten sollten sich in verschiedenen Spalten befinden. Die erste Zeile in der Datei sollte folgendermaßen aussehen:

„Datum" „Zeit" „Offen" „Hoch" „Niedrig" „Geschlossen" „Volumen“

Die Kopfzeile in NSDT v.6 weist ein anderes Aussehen auf, das es der Applikation erlaubt, die Datei zu akzeptieren. Das bedeutet, dass Datum und Zeit sich in der selben Spalte befinden sollten.

Datum,Offen,Hoch,Niedrig,Geschlossen,Volumen

MetaTrader 5 erlaubt es Nutzern, Angebote in *.csv Dateien zu speichern. Die Daten sehen in der Datei dabei wie folgt aus:

Abb. 1. Vom MetaTrader 5-Terminal gespeicherte Daten

Abb. 1. Vom MetaTrader 5-Terminal gespeicherte Daten


Jedoch können wir die Kopfzeile nicht einfach so editieren, da das Datum ein anderes Format aufweisen muss. Für NSDT v.5:

TT,MM.JJJJ,hh:mm,Offen,Hoch,Niedrig,Geschlossen,Volumen
Für NSDT v.6:
TT/MM/JJJJ,hh:mm,Offen,Hoch,Niedrig,Geschlossen,Volumen

Dropdown-Listen werden in den externen Scriptparametern verwendet, mit deren Hilfe Nutzer notwendige Formate auswählen können. Neben der Auswahl der Header- und Datumsformate werden wir dem Nutzer die Möglichkeit geben, eine Reihe von Symbolen und Daten auszuwählen, mit denen sie Dateien beschreiben können. Um dies zu tun, werden wir drei Versionen bereitstellen:

  • Die Daten nur auf das aktuelle Symbol schreiben - in dem Chart (NUR DAS AKTUELLE SYMBOL), in dem das Script ausgeführt worden ist.
  • Die Daten auf das Symbol schreiben, das sich im Market Watch-Fenster befindet (MARKETWATCH-SYMBOLE).
  • Die Daten auf alle Symbole schreiben, die auf dem Server zur Verfügung stehen (ALLE SYMBOLE DER LSITE).

Lassen Sie uns den folgende Code vor den externen Parametern im Scriptcode eingeben, um solch eine Liste zu erstellen:

//_________________________________
// HEADER_FORMATS_ENUMERATION
enum FORMAT_HEADERS
  {
   NSDT_5 = 0, // "Date" "Time" "Open" "High" "Low" "Close" "Volume"
   NSDT_6 = 1  // Date,Open,High,Low,Close,Volume
  };
//---
//___________________________
// ENUMERATION_OF_DATA_FORMATS
enum FORMAT_DATETIME
  {
   SEP_POINT1 = 0, // dd.mm.yyyy hh:mm
   SEP_POINT2 = 1, // dd.mm.yyyy, hh:mm
   SEP_SLASH1 = 2, // dd/mm/yyyy hh:mm
   SEP_SLASH2 = 3  // dd/mm/yyyy, hh:mm
  };
//---
//____________________________
// ENUMERATION_OF_FILING_MODES
enum CURRENT_MARKETWATCH
  {
   CURRENT          = 0, // ONLY CURRENT SYMBOLS
   MARKETWATCH      = 1, // MARKETWATCH SYMBOLS
   ALL_LIST_SYMBOLS = 2  // ALL LIST SYMBOLS
  };

Mehr Hinweise zu Aufzählungen finden Sie unter MQL5-Verweise.


3. Externe Programmparameter

Nun können wir eine vollständige Liste aller externen Scriptparameter erstellen:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| EXTERNAL_PARAMETERS                                              |
//+------------------------------------------------------------------+
input datetime            start_date     = D'01.01.2011'; // Start Date
input datetime            end_date       = D'18.09.2012'; // End Date
input FORMAT_HEADERS      format_headers = NSDT_5;     // Format Headers
input FORMAT_DATETIME     format_date    = SEP_POINT2; // Format Datetime
input CURRENT_MARKETWATCH curr_mwatch    = CURRENT;    // Mode Write Symbols
input bool                clear_mwatch   = true;        // Clear Market Watch
input bool                show_progress  = true;        // Show Progress (%)

Externe Parameter erfüllen folgende Funktionen:

  • Nutzer können ein Datumsintervall mithilfe der beiden Parameter Datum Starten (start_date) und Datum Beenden (end_date) definieren.
  • Die Header Formatieren-Dropdown-Liste (format_headers) erlaubt es Nutzern, ein Headerformat auszuwählen.
  • Die Datum Formatieren-Dropdown-Liste (format_date) erlaubt es Nutzern, einen Zeit- und Datumsformat auszuwählen.
  • Die Write Symbols-Dropdown-Liste (curr_mwatch) erlaubt es Nutzern, die Anzahl an Symbolen zum Ablegen auszuwählen.
  • Falls der Market Watch Löschen-Parameter (clear_mwatch) auf wahr eingestellt ist, können Nutzer alle Symbole des Market Watch-Fensters löschen, nachdem diese abgelegt wurden. Dies betrifft nur die Chart-Symbole, die momentan nicht aktiv sind.
  • Der Fortschritte Anzeigen-Parameter (%) (show_progress) zeigt den Prozess des Ablegens im Datenfeld an. Das Ablegen geht schneller vonstatten, falls dieser Parameter deaktiviert ist.

Nachfolgend sehen Sie den Zustand der externen Parameter während des Startvorgangs.

Abb. 2. Externe Parameter der Applikation

Abb. 2. Externe Parameter der Applikation


4. Von Nutzern eingegebene Parameter überprüfen

Lassen Sie uns eine Funktion kreieren, die die Parameter überprüft, die seitens der Nutzer vor dem Basiscode eingegeben worden sind. Beispielsweise sollte das Startdatum im Datum Starten-Parameter ein früheres als das des Datum Beenden-Parameters sein. Das Kopfzeilenformat sollte mit den Zeit- und Datumsformaten übereinstimmen. Falls ein Nutzer beim Einstellen der Parameter einen Fehler begangen hat, wird die folgende Warnung auftauchen und das Programm stoppen:

Beispielhafter Warnhinweis:

Abb. 3. Warnung betreffend einen unkorrekt spezifizierten Wert

Abb. 3. Beispielhafter Warnhinweis betreffend einen unkorrekt spezifizierten Parameter


ValidationParameters() function:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| CHECKING_CORRECTNESS_OF_PARAMETERS                               |
//+------------------------------------------------------------------+
bool ValidationParameters()
  {
   if(start_date>=end_date)
     {
      MessageBox("The start date should be earlier than the ending one!\n\n"
                 "Application cannot continue. Please retry.",
                 //---
                 "Parameter error!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_5 && 
      (format_date==SEP_POINT1 || format_date==SEP_SLASH1))
     {
      MessageBox("For the headers of the following format:\n\n"
                 "\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\"\n\n"
                 "Date/time format can be selected out of two versions:\n\n"
                 "dd.mm.yyyy, hh:mm\n"
                 "dd/mm/yyyy, hh:mm\n\n"
                 "Application cannot continue. Please retry.",
                 //---
                 "Header and date/time formats do not match!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   if(format_headers==NSDT_6 && 
      (format_date==SEP_POINT2 || format_date==SEP_SLASH2))
     {
      MessageBox("For the headers of the following format:\n\n"
                 "Date,Open,High,Low,Close,Volume\n\n"
                 "Date/time format can be selected out of two versions:\n\n"
                 "dd.mm.yyyy hh:mm\n"
                 "dd/mm/yyyy hh:mm\n\n"
                 "Application cannot continue. Please retry.",
                 //---
                 "Header and date/time formats do not match!",MB_ICONERROR);
      //---
      return(true);
     }
//---
   return(false);
  }


5. Globale Variablen

Als Nächstes sollten wir alle globalen Variablen und Arrays bestimmen, die für das Script genutzt werden:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| GLOBAL_VARIABLES_AND_ARRAYS                                      |
//+------------------------------------------------------------------+
MqlRates rates[]; // Array for copying data
//---
string symbols[]; // Symbol array
//---
// Array of graphic object names
string arr_nmobj[22]=
  {
   "fon","hd01",
   "nm01","nm02","nm03","nm04","nm05","nm06","nm07","nm08","nm09","nm10",
   "nm11","nm12","nm13","nm14","nm15","nm16","nm17","nm18","nm19","nm20"
  };
//---
// Array of displayed text containing graphic objects
string arr_txtobj[21];
//---
string path="";         // File path
int cnt_symb=0;         // Number of symbols
int sz_arr_symb=0;      // Symbol array size
int bars=0;             // Number of bars according to the specified TF
int copied_bars=0;      // Number of bars copied for writing
double pgs_pcnt=0;      // Writing progress
int hFl=INVALID_HANDLE;  // File handle
//---
string   // Variables for data formatting
sdt="",  // Date line
dd="",   // Day
mm="",   // Month
yyyy="", // Year
tm="",   // Time
sep="";  // Separator
//---
int max_bars=0; // Maximum number of bars in the terminal settings
//---
datetime
first_date=0,        // First available data in a specified period
first_termnl_date=0, // First available data in the terminal's database
first_server_date=0, // First available data in the server's database
check_start_date=0;  // Checked correct date value


6. Informationsfenster

Nun sollten wir uns den Elementen zuwenden, die im Informationsfenster angezeigt werden. Dabei können drei Typen von grafischen Objekten als Hintergrund verwendet werden.

  • Bei dem einfachsten dieser drei handelt es sich um das "Rectangle Label“ (OBJ_RECTANGLE_LABEL).
  • Falls Sie Ihrem Interface einen persönlicheren Touch verleihen wollen, sollten Sie das „Bitmap“-Objekt (OBJ_BITMAP) in Erwägung ziehen.
  • Das „Edit“-Objekt (OBJ_EDIT) kann ebenso als Hintergrund dienen. Aktivieren Sie die „Nur lesen“-Eigenschaft, um zu verhindern, dass Texte eingegeben werden können. Das „Edit“-Objekt hat noch einen anderen Vorteil. Falls Sie ein Informationsfenster in einem Expert Advisior kreiert haben und Sie wollen, dass es während der Tests (Visualisierungsmodus) den gleich Look aufweist, so wird Ihnen dies lediglich durch den letzten Typ ermöglicht. Das heißt, dass weder OBJ_RECTANGLE_LABEL, noch OBJ_BITMAP während der Tests via Visualisierungsmodus angezeigt werden.

Obwohl in unserem Fall nur ein Script und kein Expert Advisor entwickelt wird, wird uns ein OBJ_EDIT-Hintergrund als Beispiel dienen. Die untere Abbildung zeigt das Ergebnis:

Abb. 4. Informationsfenster

Abb. 4. Informationsfenster


Lassen Sie uns all die im Fenster abgebildeten Daten betrachten:

  • Symbol (momentan/gesamt) – Symbol, auf das die Daten momentan heruntergeladen, kopiert oder geschrieben werden. Die linke Nummer in den Klammern zeigt die aktuelle Symbolanzahl an. Die rechte Nummer gibt die allgemeine Nummer an Symbolen an, mit denen das Script arbeiten wird.
  • Pfad-Symbol – Symbolpfad oder -kategorie, zu dem es gehört. Wenn Sie das Kontextmenü via Rechtsklick im Market Watch-Fenster aufrufen und „Symoble...“ auswählen, wird ein Fenster erscheinen, das alle Symbole auflistet. Mehr Informationen finden Sie im Benutzerhandbuch des Terminals.
  • Zeitrahmen – Periode (Zeitrahmen). Gibt den Zeitrahmen an, in welchem das Script gestartet wird.
  • Startzeitpunkt – Von einem Nutzer mittels Script-Parametern spezifiziertes Startdatum.
  • Erstes Datum (H1) – Gibt das erste mögliche Datum (Balken) des aktuellen Zeitrahmens an.
  • Erstes Terminal-Datum (M1) – Gibt das erste mögliche Datum des M1-Zeitrahmens des Terminals an.
  • First Server-Datum (M1) – Gibt das erste mögliche Datum des M1-Zeitrahmens des Servers an.
  • Max. Optionsterminalbalken – Zeigt die maximale Anzahl an Balken an, die im Chart angezeigt werden sollen. Kann in den Terminaleinstellungen geändert werden.
  • Kopierte Balken – Anzahl der kopierten Balken zum Schreiben.
  • Aktueller Symbol-Fortschrittswert – Zeigt den prozentualen Wert der geschriebenen Daten des Symbols an.

Nachstehend finden Sie den Code eines derartigen Informationsfensters.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| INFORMATION_PANEL                                                |
//|------------------------------------------------------------------+
void InfoTable(int s)
  {
   int fnt_sz=8;            // Font size
   string fnt="Calibri";     // Header font
   color clr=clrWhiteSmoke;  // Color
//---
   int xH=300;
   int height_pnl=0;
   int yV1=1,yV2=12,xV1=165,xV2=335,xV3=1;
//---
   string sf="",stf="",ssf="";
   bool flg_sf=false,flg_stf=false,flg_ssf=false;
//---
   if(show_progress) { height_pnl=138; } else { height_pnl=126; }
//---
   flg_sf=SeriesInfoInteger(symbols[s],_Period,SERIES_FIRSTDATE,first_date);
   flg_stf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_TERMINAL_FIRSTDATE,first_termnl_date);
   flg_ssf=SeriesInfoInteger(symbols[s],PERIOD_M1,SERIES_SERVER_FIRSTDATE,first_server_date);
//---
   if(flg_sf) { sf=TSdm(first_date); } else { sf="?"; }
   if(flg_stf) { stf=TSdm(first_termnl_date); } else { stf="?"; }
   if(flg_ssf) { ssf=TSdm(first_server_date); } else { ssf="?"; }
//---
   if(cnt_symb==0) { cnt_symb=1; }
//---
   int anchor1=ANCHOR_LEFT_UPPER,anchor2=ANCHOR_RIGHT_UPPER,corner=CORNER_LEFT_UPPER;
//---
   string path_symbol=SymbolInfoString(symbols[s],SYMBOL_PATH);
   path_symbol=StringSubstr(path_symbol,0,StringLen(path_symbol)-StringLen(symbols[s]));
//---
   arr_txtobj[0]="INFO TABLE";
   arr_txtobj[1]="Symbol (current / total) : ";
   arr_txtobj[2]=""+symbols[s]+" ("+IS(s+1)+"/"+IS(cnt_symb)+")";
   arr_txtobj[3]="Path Symbol : ";
   arr_txtobj[4]=path_symbol;
   arr_txtobj[5]="Timeframe : ";
   arr_txtobj[6]=gStrTF(_Period);
   arr_txtobj[7]="Input Start Date : ";
   arr_txtobj[8]=TSdm(start_date);
   arr_txtobj[9]="First Date (H1) : ";
   arr_txtobj[10]=sf;
   arr_txtobj[11]="First Terminal Date (M1) : ";
   arr_txtobj[12]=stf;
   arr_txtobj[13]="First Server Date (M1) : ";
   arr_txtobj[14]=ssf;
   arr_txtobj[15]="Max. Bars In Options Terminal : ";
   arr_txtobj[16]=IS(max_bars);
   arr_txtobj[17]="Copied Bars : ";
   arr_txtobj[18]=IS(copied_bars);
   arr_txtobj[19]="Progress Value Current Symbol : ";
   arr_txtobj[20]=DS(pgs_pcnt,2)+"%";
//---
   Create_Edit(0,0,arr_nmobj[0],"",corner,fnt,fnt_sz,clrDimGray,clrDimGray,345,height_pnl,xV3,yV1,2,C'15,15,15');
//---
   Create_Edit(0,0,arr_nmobj[1],arr_txtobj[0],corner,fnt,8,clrWhite,C'64,0,0',345,12,xV3,yV1,2,clrFireBrick);
//---
   Create_Label(0,arr_nmobj[2],arr_txtobj[1],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2,0);
   Create_Label(0,arr_nmobj[3],arr_txtobj[2],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2,0);
//---
   Create_Label(0,arr_nmobj[4],arr_txtobj[3],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*2,0);
   Create_Label(0,arr_nmobj[5],arr_txtobj[4],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*2,0);
//---
   Create_Label(0,arr_nmobj[6],arr_txtobj[5],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*3,0);
   Create_Label(0,arr_nmobj[7],arr_txtobj[6],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*3,0);
//---
   Create_Label(0,arr_nmobj[8],arr_txtobj[7],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*4,0);
   Create_Label(0,arr_nmobj[9],arr_txtobj[8],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*4,0);
//---
   Create_Label(0,arr_nmobj[10],arr_txtobj[9],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*5,0);
   Create_Label(0,arr_nmobj[11],arr_txtobj[10],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*5,0);
//---
   Create_Label(0,arr_nmobj[12],arr_txtobj[11],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*6,0);
   Create_Label(0,arr_nmobj[13],arr_txtobj[12],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*6,0);
//---
   Create_Label(0,arr_nmobj[14],arr_txtobj[13],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*7,0);
   Create_Label(0,arr_nmobj[15],arr_txtobj[14],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*7,0);
//---
   Create_Label(0,arr_nmobj[16],arr_txtobj[15],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*8,0);
   Create_Label(0,arr_nmobj[17],arr_txtobj[16],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*8,0);
//---
   Create_Label(0,arr_nmobj[18],arr_txtobj[17],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*9,0);
   Create_Label(0,arr_nmobj[19],arr_txtobj[18],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*9,0);
//---
   if(show_progress)
     {
      Create_Label(0,arr_nmobj[20],arr_txtobj[19],anchor2,corner,fnt,fnt_sz,clr,xV1,yV1+yV2*10,0);
      Create_Label(0,arr_nmobj[21],arr_txtobj[20],anchor2,corner,fnt,fnt_sz,clr,xV2,yV1+yV2*10,0);
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| CREATING_LABEL_OBJECT                                            |
//+------------------------------------------------------------------+
void Create_Label(long   chrt_id,   // chart id
                  string lable_nm,  // object name
                  string rename,    // displayed name
                  long   anchor,    // anchor point
                  long   corner,    // attachment corner
                  string font_bsc,  // font
                  int    font_size, // font size
                  color  font_clr,  // font color
                  int    x_dist,    // X scale coordinate
                  int    y_dist,    // Y scale coordinate
                  long   zorder)    // priority
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_LABEL,0,0,0)) // creating object
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,rename);          // set name
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);        // set font
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);      // set font color
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ANCHOR,anchor);       // set anchor point
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);       // set attachment corner
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);  // set font size
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);    // set X coordinates
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);    // set Y coordinates
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);     // unable to highlight the object, if FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);       // Higher/lower priority
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");         // no tooltip, if "\n"
     }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| CREATING_EDIT_OBJECT                                             |
//+------------------------------------------------------------------+
void Create_Edit(long   chrt_id,       // chart id
                 int    nmb_win,       // window (subwindow) index
                 string lable_nm,      // object name
                 string text,          // displayed text
                 long   corner,        // attachment corner
                 string font_bsc,      // font
                 int    font_size,     // font size
                 color  font_clr,      // font color
                 color  font_clr_brd,  // font color
                 int    xsize,         // width
                 int    ysize,         // height
                 int    x_dist,        // X scale coordinate
                 int    y_dist,        // Y scale coordinate
                 long   zorder,        // priority
                 color  clr)           // background color
  {
   if(ObjectCreate(chrt_id,lable_nm,OBJ_EDIT,nmb_win,0,0)) // creating object
     {
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TEXT,text);                     // set name
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_CORNER,corner);                // set attachment corner
      ObjectSetString(chrt_id,lable_nm,OBJPROP_FONT,font_bsc);                 // set font
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ALIGN,ALIGN_CENTER);         // center alignment
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_FONTSIZE,font_size);           // set font size
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_COLOR,font_clr);               // font color
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BORDER_COLOR,font_clr_brd);    // background color
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_BGCOLOR,clr);                  // background color
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XSIZE,xsize);                  // width
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YSIZE,ysize);                  // height
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_XDISTANCE,x_dist);             // set X coordinate
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_YDISTANCE,y_dist);             // set Y coordinate
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_SELECTABLE,false);              // unable to highlight the object, if FALSE
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_ZORDER,zorder);                // Higher/lower priority
      ObjectSetInteger(chrt_id,lable_nm,OBJPROP_READONLY,true);                // Read only
      ObjectSetString(chrt_id,lable_nm,OBJPROP_TOOLTIP,"\n");                  // no tooltip if "\n"
     }
  }

Nachdem die Script-Operation beendet wurde oder das Script durch einen Nutzer vorzeitig gelöscht wurde, sollten alle vom Script kreierten Grafikobjekte gelöscht werden. Mithilfe der folgenden Instruktionen finden Sie heraus, wie das funktioniert.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| DELETE_ALL_GRAPHICAL_OBJECTS_CREATED_BY_THE_SCRIPT               |
//+------------------------------------------------------------------+
void DelAllScriptObjects()
  {
// Receive the size of graphical object names array
   int sz_arr1=ArraySize(arr_nmobj);
//---
// Delete all objects
   for(int i=0; i<sz_arr1; i++)
     { DelObjbyName(arr_nmobj[i]);  }
  }
//____________________________________________________________________
//+------------------------------------------------------------------+
//| DELETE_OBJECTS_BY_NAME                                           |
//+------------------------------------------------------------------+
int DelObjbyName(string Name)
  {
   int nm_obj=0;
   bool res=false;
//---
   nm_obj=ObjectFind(ChartID(),Name);
//---
   if(nm_obj>=0)
     {
      res=ObjectDelete(ChartID(),Name);
      //---
      if(!res) { Print("Object deletion error: - "+ErrorDesc(Error())+""); return(false); }
     }
//---
   return(res);
  }


7. Applikationshauptblock

Die Hauptaufgabe des Scripts ist die folgende: OnStart(). Diese Funktion dient dazu, anderen Funktion das Ausführen zu befehlen. Die Details der Programmoperationen können im Code eingesehen werden:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| SCRIPT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
//+------------------------------------------------------------------+
void OnStart()
  {
// If user-defined parameters are incorrect,
// error message is shown and the program is closed
   if(ValidationParameters()) { return; }
//---
   max_bars=TerminalInfoInteger(TERMINAL_MAXBARS); // Receive available number of bars in the window
//---
   GetSymbolsToArray();           // Filling symbol array with names
   sz_arr_symb=ArraySize(symbols); // Receive symbol array size
//---
   SetSeparateForFormatDate();    // Set a separator for date format
//---
// Revise all symbols and write their data to file
   for(int s=0; s<=sz_arr_symb-1; s++)
     {
      copied_bars=0; // Reset copied bars variable to zero for writing
      pgs_pcnt=0.0;  // Reset variable of the symbol data writing progress 
      //---
      InfoTable(s); ChartRedraw();
      //---
      // Receive current symbol data
      int res=GetDataCurrentSymbol(s);
      //---
      if(res==0) { BC } // If zero, break the loop or start the next iteration
      //---
      if(res==2)        // Program operation interrupted by user
        {
         DelAllScriptObjects(); // Deleted objects created by the script from the chart
         //---
         Print("------\nUser deleted the script!"); break;
        }
      //---
      // Receive the path for creating the file and create directories for them
      // If the string is empty, break the loop or start the next iteration
      if((path=CheckCreateGetPath(s))=="") { BC }
      //---
      WriteDataToFile(s); // Write data to file
     }
//---
// Delete symbols from Market Watch window if necessary
   DelSymbolsFromMarketWatch();
//---
// Delete objects created by the script from the chart
   Sleep(1000); DelAllScriptObjects();
  }

Lassen Sie uns die Funktionen ansehen, in den sich die Hauptaktivitäten ereignen.

Das Array des Symbols (symbols[]) ist bis zum Rand mit Symbolnamen gefüllt (GetSymbolsToArray()-Funktion). Die Größe des Arrays als auch die Anzahl seiner Symbole basiert auf dem vom Nutzer spezifizierten Write-Symbols-Parameter (curr_mwatch).

Für den Fall, dass ein Nutzer lediglich die Daten eines einzelnen Symbols besitzt, beträgt diese Größe 1.

ArrayResize(symbols,1); // Set the array size to be equal to 1
symbols[0]=_Symbol;     // Specify the current symbol's name

Falls ein Nutzer die Daten aller Symbole des Market Watch-Fensters oder aller möglichen Symbole erhalten will, wird die Array-Größe durch die folgende Funktion definiert:

int SymbolsTotal(
   bool selected   // true – only MarketWatch symbols
);

Um zu vermeiden, dass zwei Blöcke für zwei Varianten mit beinahe identischem Code kreiert werden, werden wir uns der MWatchOrAllList() pointer-Funktion bedienen, die entweder ein wahr oder falsch ausgibt. Dieser Wert definiert woher die Symbolleiste stammen soll - nur vom Market Watch-Fenster (wahr) oder von der allgemeinen Liste aller Symbole (falsch).

//____________________________________________________________________
//+------------------------------------------------------------------+
//| POINTER_TO_MARKET_WATCH_WINDOW_OR_TO_COMMON_LIST                 |
//+------------------------------------------------------------------+
bool MWatchOrAllList()
  {
   if(curr_mwatch==MARKETWATCH) { return(true); }
   if(curr_mwatch==ALL_LIST_SYMBOLS) { return(false); }
//---
   return(true);
  }

Nachdem die Anzahl der Symbole (Loop) erhalten wurde, sollten wir die gesamte Liste durcharbeiten und die Symbolnamen in das Array eingeben, wobei jede Iteration die Größe um eins anwachsen lassen wird. Der Symbolname wird seinerseits von der Indexzahl bezogen, indem die SymbolName()-Funktion angewendet wird.

int SymbolName(
   int pos,        // list index number
   bool selected   // true – only MarketWatch symbols
);

Die MWatchOrAllList() pointer-Funktion wird ferner in der SymbolName()-Funktion dafür benutzt, die Symbolliste auszuwählen. Der gesamte Code der GetSymbolsToArray()-Funktion:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| FILLING_SYMBOL_ARRAY_WITH_NAMES                                  |
//+------------------------------------------------------------------+
void GetSymbolsToArray()
  {
// If only the current symbol data is required
   if(curr_mwatch==CURRENT)
     { ArrayResize(symbols,1); symbols[0]=_Symbol; }
//---
// If data on all symbols  from Market Watch window or
// or the entire symbol list is required
   if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS)
     {
      // Receive the number of symbols in Market Watch window
      cnt_symb=SymbolsTotal(MWatchOrAllList());
      //---
      for(int i=0; i<=cnt_symb-1; i++)
        {
         string nm_symb="";
         //---
         ArrayResize(symbols,i+1); // Increase the array size by one once again
         //---
         // Receive a name of a symbol from Market Watch window
         nm_symb=SymbolName(i,MWatchOrAllList());
         symbols[i]=nm_symb; // Put the symbol name into the array
        }
     }
  }

Die SetSeparateForFormatDate()-Funktion ist relativ simpel. Sie wird dafür benutzt, um zu definieren, welcher Separator im Datum genutzt werden soll - abhängig von der Wahl des Nutzers betreffend den Datum Formatieren-Parameter (format_date) der Dropdown-Liste.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| DEFINING_SEPARATOR_FOR_DATE_FORMAT                               |
//+------------------------------------------------------------------+
void SetSeparateForFormatDate()
  {
   switch(format_date)
     {
      case SEP_POINT1 : case SEP_POINT2 : sep="."; break; // Full point as a separator
      case SEP_SLASH1 : case SEP_SLASH2 : sep="/"; break; // Slash as a separator
     }
  }

Anschließend kommt es zu einer Schleife samt verschiedener Überprüfungen. Sollten diese erfolgreich sein, so werden die Daten in die Datei hineingeschrieben. Andererseits, falls die Schleife bricht, so werden entweder alle Objekte des Charts gelöscht und das Script (im Falle von einem Symbol) entfernt oder aber (im Falle von mehr als einem Symbol) die nächste Iteration beginnt. Jedes Symbol des symbols[]-Arrays wird regelmäßig in der Schleife aufgerufen. Die Indexzahl wird dabei jeder Funktion der Schleife zugesandt. Auf diese Weise wird die Genauigkeit der Funktion gewährleistet.

Die Daten des aktuellen Symbols der Schleife werden zum Beginn jeder Iteration vom Schleifentextkörper empfangen. Dies ist die Aufgabe der GetDataCurrentSymbol()-Funktion. Sehen wir uns an, was mit dieser Funktion passiert.

Bevor die Symboldaten in das rate[]-Array kopiert werden, wird zunächst die Datenverfügbarkeit mithilfe der CheckLoadHistory()-Funktion überprüft. Die Entwickler stellen diese Funktion als Anschauungsobjekt zur Verfügung. Die ursprüngliche Version kann hierbei unter MQL5-Verweise eingesehen werden. Ich habe lediglich einige geringfügige Verbesserung vorgenommen, die die Scriptnutzung erleichtern sollen. Da das Verweise-Verzeichnis eine detaillierte Beschreibung enthält (ich schlage an dieser Stelle vor, diese zu studieren), werde ich meine - beinahe identische - Beschreibung nicht noch einmal zusätzlich hinzufügen. Sie kann außerdem im Code samt detaillierter Kommentare gefunden werden.

Eine Sache, die es noch zu erwähnen gilt: Die CheckLoadHistory()-Funktion gibt einen Fehler oder einen erfolgreichen Ausführungscode aus, durch den die entsprechende Nachricht vom switch-Operatorblock im Journal gespeichert wird. Abhängig vom empfangenen Code wird die GetDataCurrentSymbol()-Funktion ihre Operation fortsetzen, oder aber den Code wieder ausgeben.

Falls alles läuft wie geplant, werden die Verlaufsdaten durch die CopyRates()-Funktion kopiert. Die Array-Größe wird als globale Variable gespeichert. Dann wird die Funktion beendet und der Code 1 zurückgegeben. Falls ein Problem auftritt, stoppt die Funktion ihre Switch Operator-Operationen und gibt den Code 0 oder 2 zurück.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| RECEIVE_SYMBOL_DATA                                              |
//+------------------------------------------------------------------+
int GetDataCurrentSymbol(int s)
  {
   Print("------\n№"+IS(s+1)+" >>>"); // Save a symbol number in the journal
//---
// Check and download the necessary amount of requested data
   int res=CheckLoadHistory(s,_Period);
//---
   InfoTable(s); ChartRedraw(); // Update the data in the data table
//---
   switch(res)
     {
      case -1 : Print("Unknown symbol "+symbols[s]+" (code: -1)!");                        return(0);
      case -2 :
         Print("Number of requested bars exceeds the maximum number that can be displayed on a chart (code: -2)!...\n"
               "...The available amount of data will be used for writing.");                break;
      //---
      case -3 : Print("Execution interrupted by user (code: -3)!");                         return(2);
      case -4 : Print("Download failed (code: -4)!");                                      return(0);
      case  0 : Print("All symbol data downloaded (code: 0).");                             break;
      case  1 : Print("Time series data is sufficient (code: 1).");                          break;
      case  2 : Print("Time series created based on existing terminal data (code: 2).");      break;
      //---
      default : Print("Execution result is not defined!");
     }
//---
// Copy data to the array
   if(CopyRates(symbols[s],_Period,check_start_date,end_date,rates)<=0)
     { Print("Error when copying symbol data "+symbols[s]+" - ",ErrorDesc(Error())+""); return(0); }
   else
     {
      copied_bars=ArraySize(rates); // Receive array size
      //---
      Print("Symbol: ",symbols[s],"; Timeframe: ",gStrTF(_Period),"; Copied bars: ",copied_bars);
     }
//---
   return(1); // Return 1, if all is well
  }

Danach befindet sich das Programm wieder im Schleifentextköprer der OnStart()-Funktion. Der Code wird durch die lokale Variable res zugewiesen und hinsichtlich seines Wertes überprüft. Der Wert 0 steht an dieser Stelle für einen Fehler. Das bedeutet, dass die Daten des aktuellen Symbols in der Schleife nicht geschrieben werden können. Der Fehler wird im Journal notiert, worauf eine Entscheidung getroffen werden muss, ob die Schleife mit einem Bruch (Bruch) zu versehen ist, oder aber ob die nächste Iteration beginne soll (Fortsetzen).

if(res==0) { BC } // If zero, the loop is interrupted or the next iteration starts

Die oben befindliche Codezeile zeigt an, dass diese Auswahl durch einige mysteriöse BC-Zeichen ausgeführt wird. Hierbei handelt es sich um eine Macro-Erweiterung. Mehr Informationen hierzu finden Sie unter MQL5-Verweise. Im vorliegenden Artikel sei lediglich erwähnt, dass alle Ausdrücke (in einer Zeile eingegeben) in einen kurzen Eintrag kopiert werden können - siehe obiges Beispiel (BC). In manchen Fällen ist diese Methode sogar noch komfortabler als eine Funktion. Im vorliegenden Fall sieht dies wie folgt aus:

// Macro expansion with further action selection
#define BC if(curr_mwatch==CURRENT) { break; } if(curr_mwatch==MARKETWATCH || curr_mwatch==ALL_LIST_SYMBOLS) { continue; }

Unten sehen wir andere Beispiele für Macro-Erweiterungen, die in diesem Script benutzt werden.

#define nmf __FUNCTION__+": " // Macro expansion of the function name before sending the message to the journal
//---
#define TRM_DP TerminalInfoString(TERMINAL_DATA_PATH) // Folder for storing the terminal data

Falls GetDataCurrentSymbol() 2 wiedergibt, wurde das Programm durch den Nutzer gelöscht. MQL5 besitzt eine sogenannte IsStopped()-Funktion, die solch einen Fall erkennen kann. Diese Funktion hilft Schleifen dabei, die Operation des Programms korrekt und rechtzeitig zu stoppen. Falls die Funktion den Wert wahr zurückgibt, existiert ein Zeitfenster von ungefähr drei Sekunden, um alle Aktionen durchzuführen, bevor das Programm gewaltsam gelöscht wird. In unserem Fall werden alle grafischen Objekte entfernt und die folgende Nachricht wird im Journal verzeichnet:

if(res==2) // Program execution interrupted by user
   {
    DelAllScriptObjects(); // Delete all objects created by the script from the chart
    //---
    Print("------\nUser deleted the script!"); break;
   }


8. Ordner anlegen und Daten ablegen

Die Funktion CheckCreateGetPath() überprüft die Existenz eines root-data-Ordners. Lassen Sie uns diesen DATA_OHLC nennen und in C:\Metatrader 5\MQL5\Files platzieren. Er wird letztendlich verschiedene Ordner mit Symbolnamen enthalten. Die Dateien, um die Daten zu schreiben, werden hier angelegt.

Falls der root-Ordner oder der Ordner für das aktuelle Symbol in der Loop nicht existiert, wird die Funktion ihn entsprechend anlegen. Falls alles wie geplant funktioniert, gibt die Funktion eine Zeichenkette aus, die den Pfad zum Kreieren einer Datei enthält. Die Funktion gibt eine leere Zeichenkette aus, falls ein Fehler vorliegt oder der Nutzer den Versuch unternimmt, das Programm aus dem Chart zu löschen.

Der unten stehende Code enthält detaillierte Informationen, um diesen Sachverhalt verständlicher zu machen.

//____________________________________________________________________
//+------------------------------------------------------------------+
//| CHECK_DIRECTORY_AND_CREATE_NECESSARY_DATA_FOLDERS                |
//+------------------------------------------------------------------+
string CheckCreateGetPath(int s)
  {
   int i=1;
   long search=-1;
   string ffname="",lpath="";
   string file="*.csv",folder="*";
   string
   root="DATA_OHLC\\",         // Root data folder
   fSmb=symbols[s]+"\\",     // Symbol name
   fTF=gStrTF(_Period)+"\\"; // Symbol time frame
//---
   bool flgROOT=false,flgSYMBOL=false;
//---
//+------------------------------------------------------------------+
//| SEARCHING_FOR_DATA_OHLC_ROOT_FOLDER                              |
//+------------------------------------------------------------------+
   lpath=folder;
   search=FileFindFirst(lpath,ffname); // Set search handle in Metatrader 5\MQL5\Files
//---
   Print("Directory: ",TRM_DP+"\\MQL5\\Files\\");
//---
// Set the flag if the first folder is a root one
   if(ffname==root)
     { flgROOT=true; Print("Root folder "+root+" present"); }
//---
   if(search!=INVALID_HANDLE) // If search handle received
     {
      if(!flgROOT) // If the first folder is not a root one
        {
         // Sort out all files searching for the root folder
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // Execution interrupted by user
              {
               // Delete objects created by the script from the chart
               DelAllScriptObjects();
               //---
               Print("------\nUser deleted the script!"); return("");
              }
            //---
            if(ffname==root) // Set the flag if found
              { flgROOT=true; Print("Root folder "+root+" present"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Close root folder search handle
     }
   else { Print("Error when receiving the search handle or directory "+TRM_DP+" is empty: ",ErrorDesc(Error())); }
//---
//+------------------------------------------------------------------+
//| SEARCHING_SYMBOL_FOLDER                                          |
//+------------------------------------------------------------------+
   lpath=root+folder;
//---
// Set search handle in the root folder ..\Files\DATA OHLC\
   search=FileFindFirst(lpath,ffname);
//---
// Set the flag if the first folder of the current symbol
   if(ffname==fSmb) { flgSYMBOL=true; Print("Symbol folder "+fSmb+" present"); }
//---
   if(search!=INVALID_HANDLE) // If search handle is received
     {
      if(!flgSYMBOL) // If the first folder is not of the current symbol
        {
         // Sort out all the files in the root folder searching the symbol folder
         while(FileFindNext(search,ffname))
           {
            if(IsStopped()) // Execution interrupted by user
              {
               // Delete objects created by the script from the chart
               DelAllScriptObjects();
               //---
               Print("------\nUser deleted the script!"); return("");
              }
            //---
            if(ffname==fSmb) // Set the flag if found
              { flgSYMBOL=true; Print("Symbol folder"+fSmb+" present"); break; }
           }
        }
      //---
      FileFindClose(search); search=-1; // Close symbol folder search handle
     }
   else { Print("Error when receiving search handle or the directory "+path+" is empty"); }
//---
//+------------------------------------------------------------------+
//| CREATE_NECESSARY_DIRECTORIES_ACCORDING_TO_CHECK_RESULTS          |
//+------------------------------------------------------------------+
   if(!flgROOT) // If there is no DATA_OHLC... root folder
     {
      if(FolderCreate("DATA_OHLC")) // ...we should create it
        { Print("..\DATA_OHLC\\ root folder created"); }
      else
        { Print("Error when creating DATA_OHLC: root folder",ErrorDesc(Error())); return(""); }
     }
//---
   if(!flgSYMBOL) // If there is no folder of the symbol, the values of which should be received...
     {
      if(FolderCreate(root+symbols[s])) // ...we should create it
        {
         Print("..\DATA_OHLC\\" symbol folder created+fSmb+"");
         //---
         return(root+symbols[s]+"\\"); // Return the path for creating the file for writing
        }
      else
        { Print("Error when creating ..\DATA_OHLC\\ symbol folder"+fSmb+"\: ",ErrorDesc(Error())); return(""); }
     }
//---
   if(flgROOT && flgSYMBOL)
     {
      return(root+symbols[s]+"\\"); // Return the path for creating the file for writing
     }
//---
   return("");
  }

Falls die CheckCreateGetPath()-Funktion eine leere Zeile ausgibt, dann ist entweder die Schleife unterbrochen, oder die nächste Wiederholung startet mit einer bereits bekannten Makro-Erweiterung (BC):

// Receive the path for creating a file and create directories for them
// If the line is empty, the loop is interrupted or the next iteration starts
if((path=CheckCreateGetPath(s))=="") { BC }

Falls Sie bis hierher gekommen sind, so wurden die Daten erfolgreich kopiert und die Variable der Zeichenkette des Pfads enthält den Pfad, um die Datei anzulegen, die die Daten des aktuellen Symbols in die Loop schreibt.

Kreieren Sie nun die WriteDataToFile()-Funktion, um die Datei mit Daten zu versehen. [Path]+[file name] werden zu Beginn der Funktion generiert. Der Name einer Datei besteht aus dem Namen eines Symbols und dem aktuellen Zeitfenster. Beispiel: EURUSD_H1.csv. Falls eine Datei mit diesem Namen bereits existiert, so ist sie gerade geöffnet und kann beschrieben werden. Zuvor geschriebene Daten werden gelöscht werden. Stattdessen werden neue Daten geschrieben werden. Falls die Datei erfolgreich geöffnet/angelegt wurde, so wird die FileOpen()-Funktion den Handle wieder ausgeben, der zum Zugriff auf diese Datei nötig ist.

Kommen wir zum Überprüfen des Handels. Falls existent, wird die Kopfzeile geschrieben. Das Schreiben der entsprechenden Zeile hängt dabei davon ab, welcher Header seitens des Nutzers ausgewählt wurden. Danach beginnt die Hauptschleife, die die Verlaufsdaten schreibt.

Vor dem Schreiben der nächsten Zeile sollte der Nutzer eine Konvertierung in ein entsprechendes Format anordnen. Um dies zu bewerkstelligen, sollten wir den Offene Zeit-Balken erhalten und Tag, Monat, Jahr und Zeit separat mittels der Funktion StringSubstr() aussortieren. Dann sollten wir definieren, ob - abhängig vom spezifizierten Benutzerformat - Datum und Zeit in einer einzigen Spalte oder in separaten Spalten stehen sollen. Dann werden alle Teile in eine Zeile zusammengefügt, indem wir uns die StringConcatenate()-Funktion zunutze machen. Nachdem alle Zeilen geschrieben wurden, wird die Datei durch Benutzung der FileClose()-Funktion geschlossen.

Der gesamte Code der WriteDataToFile()-Funktion findet sich unten:

//____________________________________________________________________
//+------------------------------------------------------------------+
//| WRITE_DATA_TO_FILE                                               |
//+------------------------------------------------------------------+
void WriteDataToFile(int s)
  {
// Number of decimal places in the symbol price
   int dgt=(int)SymbolInfoInteger(symbols[s],SYMBOL_DIGITS);
//---
   string nm_fl=path+symbols[s]+"_"+gStrTF(_Period)+".csv"; // File name
//---
// Receive file handle for writing
   hFl=FileOpen(nm_fl,FILE_WRITE|FILE_CSV|FILE_ANSI,',');
//---
   if(hFl>0) // If the handle is received
     {
      // Write the headers
      if(format_headers==NSDT_5)
        { FileWrite(hFl,"\"Date\" ""\"Time\" ""\"Open\" ""\"High\" ""\"Low\" ""\"Close\" ""\"Volume\""); }
      //---
      if(format_headers==NSDT_6)
        { FileWrite(hFl,"Date","Open","High","Low","Close","Volume"); }
      //---
      // Write the data
      for(int i=0; i<=copied_bars-1; i++)
        {
         if(IsStopped()) // If program execution interrupted by a user
           {
            DelAllScriptObjects(); // Delete objects created by the script from the chart
            //---
            Print("------\nUser deleted the script!"); break;
           }
         //---
         sdt=TSdm(rates[i].time); // Bar open time
         //---
         // Divide the date by year, month and time
         yyyy=StringSubstr(sdt,0,4);
         mm=StringSubstr(sdt,5,2);
         dd=StringSubstr(sdt,8,2);
         tm=StringSubstr(sdt,11);
         //---
         string sep_dt_tm=""; // Separator of Date and Time columns
         //---
         // Join the data with the separator in the necessary order
         if(format_date==SEP_POINT1 || format_date==SEP_SLASH1) { sep_dt_tm=" "; }
         if(format_date==SEP_POINT2 || format_date==SEP_SLASH2) { sep_dt_tm=","; }
         //---
         // Join everything in one line
         StringConcatenate(sdt,dd,sep,mm,sep,yyyy,sep_dt_tm,tm);
         //---
         FileWrite(hFl,
                   sdt,// Date-time
                   DS_dgt(rates[i].open,dgt),      // Open price
                   DS_dgt(rates[i].high,dgt),      // High price
                   DS_dgt(rates[i].low,dgt),       // Low price
                   DS_dgt(rates[i].close,dgt),     // Close price
                   IS((int)rates[i].tick_volume)); // Tick volume price
         //---
         // Update writing progress value for the current symbol
         pgs_pcnt=((double)(i+1)/copied_bars)*100;
         //---
         // Update data in the table
         InfoTable(s); if(show_progress) { ChartRedraw(); }
        }
      //---
      FileClose(hFl); // Close the file
     }
   else { Print("Error when creating/opening file!"); }
  }

Dies war die letzte Funktion der einfachen Schleife der OnStart()-Funktion. Fall es sich nicht um das letzte Symbol gehandelt haben sollte, so beginnt alles noch einmal von vorn für das nächste Symbol. Ansonsten erfolgt ein Bruch der Schleife. Falls der Nutzer spezifiziert hat, die Symbolliste im Market Watch-Fenster in den Scriptparametern zu löschen, werden die Symbole, die momentan keinerlei aktive Charts aufweisen, durch die DelSymbolsFromMarketWatch()-Funktion gelöscht. Im Anschluss daran werden alle vom Script kreierten grafischen Objekte gelöscht und das Programm stoppt. Die Daten können nun verwendet werden.

 

Fazit

Egal, welches Programm ich auch verwendet habe, um meine Trading-Strategien zu entwickeln, ich stieß wieder und wieder auf Limitierungen, die sich meinen Ideen in den Weg stellten. Schlussendlich gelangte ich zu der Erkenntnis, dass die Programmierung entscheidend ist. MQL5 stellt daher die beste Lösung für all jene dar, die tatsächlich erfolgreich sein wollen. Gleichwohl kann man natürlich auch von anderen Programmen, die sich mit Datenanalysen und der Entwicklung von Trading-Strategien beschäftigen, Impulse für neue Ideen erhalten. Wenn ich mich nur eines einzigen Tools bedient hätte, hätte ich wesentlich mehr Zeit gebraucht, um auf all diese Ideen zu kommen.

Viel Erfolg!

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

Beigefügte Dateien |
writedatatofile.mq5 (68.96 KB)
Mit MetaTrader 5 via Named Pipes ohne DDLs kommunizieren Mit MetaTrader 5 via Named Pipes ohne DDLs kommunizieren
Viele Entwickler sehen sich mit demselben Problem konfrontiert: Wie kreiert man eine Sandboxumgebung für ein Handelsterminal ohne unsichere DLLs zu benutzen. Eine der leichtesten und zugleich sichersten Methoden besteht in der Verwendung standardisierter Named Pipes, die normale Dateioperationen gewährleisten. Diese ermöglichen eine Interprozessor-Client-Server basierte Kommunikation zwischen Programmen. Werfen Sie einen Blick auf die praktischen C++- und MQL5-Beispiele, einschließlich Server, Client, den Datenaustausch zwischen beiden sowie den Benchmark-Test.
Wie man einen Handelsroboter via MetaTrader Market ersteht Wie man einen Handelsroboter via MetaTrader Market ersteht
Jedes Produkt im MetaTrader Market kann über Handelsplattformen MetaTrader 4 und MetaTrader 5 sowie direkt auf der MQL5.com Website gekauft werden. Wählen ein Produkt aus, das Ihrem Handelsstil passt, bezahlen Sie es auf die von Ihnen bevorzugten Weise und vergessen Sie nicht, es zu aktivieren.
Schnelles Testen von Handelsideen im Diagramm Schnelles Testen von Handelsideen im Diagramm
Dieser Beitrag beschreibt eine Methode zum schnellen visuellen Testen von Handelsideen. Die Methode basiert auf der Kombination aus einem Preisdiagramm, einem Signalindikator und einem Indikator zur Bilanzberechnung. Ich möchte meine Methode zur Suche nach Handelsideen sowie die von mir genutzte Methode zum schnellen Testen dieser Ideen mit Ihnen teilen.
Neuronale Netzwerke: Von der Theorie zur Praxis Neuronale Netzwerke: Von der Theorie zur Praxis
Heutzutage hat sicherlich jeder Trader schon einmal etwas von einem neuronalen Netzwerk gehört - und weiß, wie cool es ist, diese zu benutzen. Die Mehrheit scheint zu glauben, dass es sich bei all jenen, die mit neuronalen Netzwerken operieren, um irgendwelche Übermenschen handeln würde. Mithilfe des vorliegenden Artikels verbinde ich die Absicht, Ihnen die Architektur eines neuronalen Netzwerks samt seiner Applikationen und praktischen Nutzanwendungen näherzubringen.