Erstellung und Ausgabe von Handelsberichten sowie Mitteilungsversand per SMS

Denis Zyatkevich | 2 März, 2016

Einleitung

In diesem Beitrag wird beschrieben, wie man einen Bericht über Handelsergebnisse mithilfe eines Expert-Systems, eines Indikators oder eines Skripts in Form einer Datei im HTML-Format erstellt und mittels FTP auf einen Webserver lädt. Überdies wird die Frage des Versandes von Mitteilungen über Handelsereignisse per E-Mail sowie per SMS an Mobiltelefone erörtert.

Zum besseren Verständnis des in diesem Beitrag verwendeten Materials ist es ratsam, mit HTML (der Sprache zur Auszeichnung von HyperText) vertraut zu sein.

Zur Nutzung der Möglichkeit, Berichte per FTP auf einen Webserver hoch zu laden (das kann ein beliebiger Rechner sein), muss ein solcher vorhanden sein und den Datenempfang per FTP zulassen. Zur Umsetzung des Empfangs von Mitteilungen über Handelsereignisse in Form von Kurznachrichten ist eine E-Mail-SMS-Schnittstelle erforderlich (diesen Dienst bieten sowohl die meisten Mobilfunkbetreiber als auch Fremdanbieter).


1. Erstellen eines Berichts mit anschließendem Versand über FTP

Schreiben wir also ein MQL5-Programm, das einen Handelsbericht zusammenstellt und per FTP verschickt. Wir verfassen es zunächst in Form eines Skripts, das wir anschließend als fertigen Block in Expert-Systeme und Indikatoren einfügen können. In Expert-Systemen beispielsweise können wir dieses fertige Modul zur Verarbeitung der Ereignisse Trade oder Timer nach erfolgter Handelsanfrage oder zur Festlegung einer der Operationen für das Ereignis ChartEvent verwenden. Bei Indikatoren kann dieser Block auf vergleichbare Weise in die Ereignisverarbeitungsroutinen Timer oder ChartEvent eingebunden werden.

Ein Beispiel für mithilfe dieses Programms erstellte Berichte liefern die Abbildungen 1 bis 3. Sie können über die Verknüpfung am Ende dieses Artikels auch heruntergeladen werden.

Abbildung 1. Berichtsbeispiel - Übersicht über Abschlüsse und Positionen.

Abbildung 1. Berichtsbeispiel - Übersicht über Abschlüsse und Positionen.

Abbildung 2. Berichtsbeispiel - Kontoverlaufsdiagramm.

Abbildung 2. Berichtsbeispiel - Kontoverlaufsdiagramm.

 Abbildung 3. Berichtsbeispiel - Kursdiagramm

Abbildung 3. Berichtsbeispiel - Kursdiagramm

In der Übersicht über Abschlüsse und Positionen (Abb. 1) sind die Abschlüsse der Einfachheit halber nach Positionen aufgegliedert. Links werden Umfang, Zeitpunkt und Kurs des Markteinstiegs (Eröffnung und Ausbau der Position) angezeigt, rechts die des Marktausstiegs (teilweise oder vollständige Schließung der Position). Die Richtungsumkehr einer Position weist zwei Teile auf, die Schließung der einen Position und die Eröffnung der nächsten.

Unter der Übersicht über Abschlüsse und Positionen sehen wir ein Kontoverlaufsdiagramm (die waagerechte Achse gibt den Zeitverlauf wieder) und ganz unten das Kursdiagramm zu dem aktuellen Finanzinstrument bzw. Kürzel.

Das Programm erstellt die Dateien report.html (für den Bericht) sowie picture1.gif und picture2.gif (als Bilddateien für das Kontoverlaufs- bzw. das Kursdiagramm) in dem Verzeichnis MetaTarder5_istall_dir\MQL5\Files und versendet sie, wenn dies in den Anwendungseinstellungen auf dem Ausgabegerät (Terminal) zugelassen ist, per FTP an den Server. Außerdem werden zwei weitere Dateien mit Abbildungen von Pfeilen für die Richtung der eröffneten Position (buy.gif und sell.gif) benötigt. Dazu können bereits vorgefertigte Bilder (Verknüpfung zum Herunterladen am Ende dieses Beitrags) verwendet oder in einem beliebigen Zeichenprogramm eigene angelegt werden. Diese beiden Dateien müssen auf dem Webserver in demselben Verzeichnis abgelegt werden wie die Datei report.html.

Als Eingangsparameter werden in das Programm der Anfang und das Ende des Zeitabschnitts eingegeben, für den der Bericht erstellt wird. In unserem Beispiel ist das Ende des Berichtszeitraums die aktuelle Zeit, und der Anwender kann eine beliebige Variante als Berichtszeitraum auswählen: den gesamten Zeitraum, den jeweils letzten Tag, die letzte Woche, den letzten Monat oder das letzte Jahr.

Einige Anmerkungen zur Erstellung des Berichts. Der gesamte Abschlussverlauf wird vom Handelsserver abgerufen, und die so empfangenen Abschlüsse werden der Reihe nach abgearbeitet. In dem Datenfeld (Array) deal_status[] wird die Information gespeichert, ob ein Abschluss verarbeitet wurde oder nicht. Die Kennziffern der Datenfeldelemente entspricht der Nummer des jeweiligen Abschlusses in der vom Handelsserver übermittelten Aufstellung der Abschlüsse, und die Werte der einzelnen Elemente haben folgende Bedeutung: 0 - der Abschluss wurde noch nicht verarbeitet, 1 - der Abschluss wurde bereits teilweise verarbeitet (Richtungsumkehr), 127 - der Abschluss wurde bereits vollständig verarbeitet (weitere Werte kommen nicht zum Einsatz, sie werden zur späteren Verwendung vorgehalten).

Das Datenfeld symb_list[] beinhaltet die Aufstellung der Bezeichnungen der Finanzinstrumente (Kürzel), mit denen gehandelt wurde, während in dem Datenfeld lots_list[] die Umfänge der eröffneten Positionen zu jedem Kürzel zum Zeitpunkt der Verarbeitung des jeweiligen Abschlusses aufgeführt sind. Positive Umfangswerte entsprechen langen Positionen, negative folglich kurzen. Ist der Umfangswert „0“, so verfügt dieses Kürzel über keine eröffneten Positionen. Taucht bei der Verarbeitung der Abschlüsse ein Finanzinstrument auf, das in der Aufstellung der Kürzel (im Datenfeld symb_list[]) nicht vorhanden ist, wird es hinzugefügt, und die Anzahl der aufgeführten Finanzinstrumente (die Variable symb_total) um eine Einheit heraufgesetzt.

Bei der Verarbeitung eines Abschlusses werden auch alle nachfolgenden Abschlüsse mit demselben Kürzel bis zum Abschluss oder der Richtungsumkehr der betreffenden Position analysiert. Es werden nur die Abschlüsse untersucht, deren Wert in dem Datenfeld deal_status[] unter 127 liegt. Nach Abschluss der Verarbeitung wird dem entsprechenden Element des Datenfeldes deal_status[] der Wert 127 zugeordnet bzw. 1, wenn sich der Abschluss an einer Richtungsumkehrposition befindet. Wenn der Zeitpunkt einer Eröffnung der Position in den (durch die Variablen StartTime und EndTime bestimmten) Berichtszeitraum fällt, werden die Angaben zu dieser Position (alle Ein- und Ausgänge) in dem Bericht aufgezeichnet.

Neben der Abschlussübersicht wird ein neues Diagramm zu dem aktuellen Finanzinstrument angelegt. Für dieses werden alle erforderlichen Eigenschaften ausgewiesen und es wird mithilfe der Funktion ChartScreenShot() eine Bildschirmaufnahme angefertigt, sodass wir eine Abbildung mit einem Kursdiagramm für das aktuelle Kürzel erhalten. Anschließend blenden wir in dieser Abbildung das Kursdiagramm aus und erstellen ein Kontoverlaufsdiagramm, woraufhin wir eine neue Bildschirmaufnahme anfertigen.

Nachdem die beiden Aufnahmen mit den Diagrammen erstellt und die HTML-Datei mit dem Bericht vollständig fertiggestellt ist, muss geprüft werden, ob der Versand über FTP zugelassen ist. Falls ja, werden die Dateien report.html, picture1.gif und picture2.gif mithilfe der Funktion SendFTP() in Übereinstimmung mit den in der MetaTrader 5-Anwendung auf dem Ausgabegerät angegebenen Einstellungen abgeschickt.

Wir starten das Bearbeitungsprogramm MetaQuotes Language und beginnen mit der Erstellung des Skripts. Zunächst legen wir die Konstanten fest: die Wartezeit bis zur nächsten Aktualisierung des Diagramms (in Sekunden), die Breite und Höhe des Kursdiagramms und die maximale Breite des Kontoverlaufsdiagramms. Die Auswahl des Diagrammzeitraums, in dem die Kontoverlaufskurve ausgegeben wird, erfolgt in Abhängigkeit von der Länge des Berichtszeitraums und der maximalen Breite des Diagramms. Die Diagrammbreite wird an den für das Kontoverlaufsdiagramm erforderlichen Umfang angepasst.

Die Höhe wird automatisch auf die Hälfte der Breite festgelegt. Als Konstante wird auch die Breite der senkrechten Achse angegeben, und zwar als Anzahl der Bildpunkte (Pixel), um die der Bildbereich aufgrund des Vorliegens der Ordinatenachse relativ zur Breite der Abbildung verringert wird.

#define timeout 10           // chart refresh timeout
#define Picture1_width 800   // max width of chart in report
#define Picture2_width 800   // width of price chart in report
#define Picture2_height 600  // height of price chart in report
#define Axis_Width 59        // width of vertical axis (in pixels)

Hinweis darauf, dass der Anwender aufgefordert wird, die Eingangsparameter anzugeben.

// request input parameters
#property script_show_inputs

Anlegen einer Aufzählung der Berichtszeiträume.

// enumeration of report periods
enum report_periods
  {
   All_periods,
   Last_day,
   Last_week,
   Last_month,
   Last_year
  };

Abfrage des Berichtszeitraums beim Anwender (standardmäßig der gesamte Zeitraum).

// ask for report period
input report_periods ReportPeriod=0;

Programmieren des Hauptteils der Funktion OnStart().

void OnStart()
  {

Festlegung von Anfang und Ende des Berichtszeitraums.

  datetime StartTime=0;           // beginning of report period
  datetime EndTime=TimeCurrent(); // end of report period

  // calculating the beginning of report period
  switch(ReportPeriod)
    {
     case 1:
        StartTime=EndTime-86400;    // day
        break;
     case 2:
        StartTime=EndTime-604800;   // week
        break;
     case 3:
        StartTime=EndTime-2592000;  // month
        break;
     case 4:
        StartTime=EndTime-31536000; // year
        break;
    }
  // if none of the options is executed, then StartTime=0 (entire period)

Deklarieren der in dem Programm verwendeten Variablen. Der Verwendungszweck der Variablen wird in den Kommentaren im Programmtext angegeben.

   int total_deals_number;  // number of deals for history data
   int file_handle;         // file handle
   int i,j;                 // loop counters 
   int symb_total;          // number of instruments, that were traded
   int symb_pointer;        // pointer to current instrument
   char deal_status[];      // state of deal (processed/not processed)
   ulong ticket;            // ticket of deal
   long hChart;             // chart id

   double balance;           // current balance value
   double balance_prev;      // previous balance value
   double lot_current;       // volume of current deal
   double lots_list[];       // list of open volumes by instruments
   double current_swap;      // swap of current deal
   double current_profit;    // profit of current deal
   double max_val,min_val;   // maximal and minimal value
   
   string symb_list[];       // list of instruments, that were traded
   string in_table_volume;   // volume of entering position
   string in_table_time;     // time of entering position
   string in_table_price;    // price of entering position
   string out_table_volume;  // volume of exiting position
   string out_table_time;    // time of exiting position
   string out_table_price;   // price of exiting position
   string out_table_swap;    // swap of exiting position
   string out_table_profit;  // profit of exiting position

   bool symb_flag;           // flag that instrument is in the list

   datetime time_prev;           // previous value of time
   datetime time_curr;           // current value of time
   datetime position_StartTime;  // time of first enter to position
   datetime position_EndTime;    // time of last exit from position
   
   ENUM_TIMEFRAMES Picture1_period;  // period of balance chart

Öffnen eines neuen Diagramms und Einstellung seiner Eigenschaften, dabei handelt es sich um das unten im Bericht ausgegebene Kursdiagramm.

 // open a new chart and set its properties
hChart=ChartOpen(Symbol(),0);
ChartSetInteger(hChart,CHART_MODE,CHART_BARS);            // bars chart
ChartSetInteger(hChart,CHART_AUTOSCROLL,true);            // autoscroll enabled
ChartSetInteger(hChart,CHART_COLOR_BACKGROUND,White);     // white background
ChartSetInteger(hChart,CHART_COLOR_FOREGROUND,Black);     // axes and labels are black
ChartSetInteger(hChart,CHART_SHOW_OHLC,false);            // OHLC are not shown
ChartSetInteger(hChart,CHART_SHOW_BID_LINE,true);         // show BID line
ChartSetInteger(hChart,CHART_SHOW_ASK_LINE,false);        // hide ASK line
ChartSetInteger(hChart,CHART_SHOW_LAST_LINE,false);       // hide LAST line
ChartSetInteger(hChart,CHART_SHOW_GRID,true);             // show grid
ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,true);       // show period separators
ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray);       // grid is light-gray
ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,Black);     // chart lines are black
ChartSetInteger(hChart,CHART_COLOR_CHART_UP,Black);       // up bars are black
ChartSetInteger(hChart,CHART_COLOR_CHART_DOWN,Black);     // down bars are black
ChartSetInteger(hChart,CHART_COLOR_BID,Gray);             // BID line is gray
ChartSetInteger(hChart,CHART_COLOR_VOLUME,Green);         // volumes and orders levels are green
ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,Red);       // SL and TP levels are red
ChartSetString(hChart,CHART_COMMENT,ChartSymbol(hChart)); // comment contains instrument <end segm

Anfertigung und Speichern einer Bildschirmaufnahme von dem Diagramm unter der Bezeichnung picture2.gif.

// save chart as image file
ChartScreenShot(hChart,"picture2.gif",Picture2_width,Picture2_height);

Abfrage des Abschlussverlaufs während der gesamten Existenz des Kontos.

// request deals history for entire period
HistorySelect(0,TimeCurrent());

Öffnen der Datei report.html zur Eingabe der HTML-Seite für den Bericht (in ANSI-Zeichensatz).

// open report file
file_handle=FileOpen("report.html",FILE_WRITE|FILE_ANSI);

Wir zeichnen den ersten Teil des HTML-Dokuments auf:

// write the beginning of HTML
   FileWrite(file_handle,"<html>"+
                           "<head>"+
                              "<title>Expert Trade Report</title>"+
                           "</head>"+
                              "<body bgcolor='#EFEFEF'>"+
                              "<center>"+
                              "<h2>Trade Report</h2>"+
                              "<table align='center' border='1' bgcolor='#FFFFFF' bordercolor='#7F7FFF' cellspacing='0' cellpadding='0'>"+
                                 "<tr>"+
                                    "<th rowspan=2>SYMBOL</th>"+
                                    "<th rowspan=2>Direction</th>"+
                                    "<th colspan=3>Open</th>"+
                                    "<th colspan=3>Close</th>"+
                                    "<th rowspan=2>Swap</th>"+
                                    "<th rowspan=2>Profit</th>"+
                                 "</tr>"+
                                 "<tr>"+
                                    "<th>Volume</th>"+
                                    "<th>Time</th>"+
                                    "<th>Price</th>"+
                                    "<th>Volume</th>"+
                                    "<th>Time</th>"+
                                    "<th>Price</th>"+
                                 "</tr>");

Abruf der Anzahl der Abschlüsse in der Aufstellung.

// number of deals in history
total_deals_number=HistoryDealsTotal();

Einstellung der Größe der Datenfelder symb_list[], lots_list[] und deal_status[].

// setting dimensions for the instruments list, the volumes list and the deals state arrays
ArrayResize(symb_list,total_deals_number);
ArrayResize(lots_list,total_deals_number);
ArrayResize(deal_status,total_deals_number);

Bereitstellung aller Elemente des Datenfeldes deal_status[] mit dem Wert 0 = alle noch nicht verarbeiteten Abschlüsse.

// setting all elements of array with value 0 - deals are not processed
ArrayInitialize(deal_status,0);

Einstellung der Anfangswerte des Kontoguthabens und der zur Speicherung des vorhergehenden Wertes des Kontostandes verwendeten Variablen.

balance=0;       // initial balance
balance_prev=0;  // previous balance

Einstellung des Anfangswertes der zur Speicherung der Anzahl der Finanzinstrumente in der Aufstellung verwendeten Variablen.

// number of instruments in the list
symb_total=0;

Anlegen eines Arbeitsgangs, in dem alle in der Aufstellung aufgeführten Abschlüsse der Reihe nach ausgelesen werden.

// processing all deals in history
for(i=0;i<total_deals_number;i++)
  {

Auswahl des aktuellen Abschlusses und Bezug des entsprechenden Händlerzettels.

//select deal, get ticket
ticket=HistoryDealGetTicket(i);

Ändern des Kontostandes um den Betrag des Gewinns aus dem aktuellen Abschluss.

// changing balance
balance+=HistoryDealGetDouble(ticket,DEAL_PROFIT);

Abruf des Zeitpunktes des Abschlusses; er wird im weiteren Verlauf des Öfteren benötigt.

// reading the time of deal
time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);

Wenn es sich dabei um den ersten Abschluss in der Aufstellung handelt, erfolgt eine Berichtigung der Grenzen des Berichtszeitraums sowie des Zeitraums für das Kontoverlaufsdiagramm je nach Dauer des Berichtszeitraums und der Breite des Bereichs, in dem das Diagramm erstellt wird. Einrichten der Eingangswerte des höchsten und des niedrigsten Kontostandes (diese Variablen werden später zur Festlegung der Ober- und der Untergrenze des Diagramms verwendet).

// if this is the first deal
if(i==0)
  {
   // if the report period starts before the first deal,
   // then the report period will start from the first deal
   if(StartTime<time_curr) StartTime=time_curr;
   // if report period ends before the current time,
   // then the end of report period corresponds to the current time
   if(EndTime>TimeCurrent()) EndTime=TimeCurrent();
   // initial values of maximal and minimal balances
   // are equal to the current balance
   max_val=balance;
   min_val=balance;
   // calculating the period of balance chart depending on the duration of
   // report period
   Picture1_period=PERIOD_M1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)) Picture1_period=PERIOD_M2;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*120) Picture1_period=PERIOD_M3;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*180) Picture1_period=PERIOD_M4;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*240) Picture1_period=PERIOD_M5;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*300) Picture1_period=PERIOD_M6;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*360) Picture1_period=PERIOD_M10;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*600) Picture1_period=PERIOD_M12;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*720) Picture1_period=PERIOD_M15;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*900) Picture1_period=PERIOD_M20;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*1200) Picture1_period=PERIOD_M30;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*1800) Picture1_period=PERIOD_H1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*3600) Picture1_period=PERIOD_H2;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*7200) Picture1_period=PERIOD_H3;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*10800) Picture1_period=PERIOD_H4;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*14400) Picture1_period=PERIOD_H6;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*21600) Picture1_period=PERIOD_H8;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*28800) Picture1_period=PERIOD_H12;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*43200) Picture1_period=PERIOD_D1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*86400) Picture1_period=PERIOD_W1;
   if(EndTime-StartTime>(Picture1_width-Axis_Width)*604800) Picture1_period=PERIOD_MN1;
   // changing the period of opened chart
   ChartSetSymbolPeriod(hChart,Symbol(),Picture1_period);
  }

Handelt es sich bei diesem Abschluss nicht um den ersten, wird ein Objekt der Art „Linie“ erstellt, mit dessen Hilfe das Kontoverlaufsdiagramm angelegt wird. Die Linie wird nur dann gezeichnet, wenn mindestens eines ihrer Enden in dem Berichtszeitraum liegt. Fallen beide Enden dort hinein, wird die Linie „dick“ dargestellt. Die Linienfarbe (grün) muss angegeben werden. Überschreitet der Kontostand die Ober- oder Untergrenze des dargestellten Bereichs, so sind diese Grenzen entsprechend anzupassen.

else
  // if this is not the first deal
  {
   // plotting the balance line, if the deal is in the report period,
   // and setting properties of the balance line
   if(time_curr>=StartTime && time_prev<=EndTime)
     {
      ObjectCreate(hChart,IntegerToString(i),OBJ_TREND,0,time_prev,balance_prev,time_curr,balance);
      ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_COLOR,Green);
      // if both ends of line are in the report period,
      // it will be "thick"
      if(time_prev>=StartTime && time_curr<=EndTime)
        ObjectSetInteger(hChart,IntegerToString(i),OBJPROP_WIDTH,2);
     }
   // if new value of balance exceeds the range
   // of minimal and maximal values, it must be adjusted
   if(balance<min_val) min_val=balance;
   if(balance>max_val) max_val=balance;
  }

Eintragen des vorhergehenden Wertes der Zeit in die entsprechende Variable.

// changing the previous time value
time_prev=time_curr;

Wenn der Abschluss noch nicht verarbeitet wurde, geschieht das jetzt.

// if the deal has not been processed yet
if(deal_status[i]<127)
  {

Wenn der Abschluss mit dem Kontostand verrechnet wurde und im Berichtszeitraum liegt, wird in dem Bericht eine entsprechende Zeile ausgegeben. Der Abschluss wird als verarbeitet gekennzeichnet.

// If this deal is balance charge
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BALANCE)
  {
   // if it's in the report period - write the corresponding string to report.
   if(time_curr>=StartTime && time_curr<=EndTime)
     FileWrite(file_handle,"<tr><td colspan='9'>Balance:</td><td align='right'>",HistoryDealGetDouble(ticket,DEAL_PROFIT),
     "</td></tr>");
   // mark deal as processed
   deal_status[i]=127;
  }

Wenn es sich bei dem Abschluss um einen Kauf oder Verkauf handelt, überprüfen wir, ob dieses Kürzel in dem Datenfeld mit der Aufstellung der Finanzinstrumente (symb_list[]) vorhanden ist. Falls nicht, wird es dort angelegt. Die Variable symb_pointer verweist auf das Element im Datenfeld symb_list[], in dem sich die Bezeichnung des Kürzels für den aktuellen Abschluss befindet.

// if this deal is buy or sell
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY || HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
  {
   // check if there is instrument of this deal in the list
   symb_flag=false;
   for(j=0;j<symb_total;j++)
     {
      if(symb_list[j]==HistoryDealGetString(ticket,DEAL_SYMBOL))
        {
         symb_flag=true;
         symb_pointer=j;
        }
     }
   // if there is no instrument of this deal in the list
   if(symb_flag==false)
     {
      symb_list[symb_total]=HistoryDealGetString(ticket,DEAL_SYMBOL);
      lots_list[symb_total]=0;
      symb_pointer=symb_total;
      symb_total++;
     }

Einstellen der Eingangswerte der zur Speicherung des Anfangs und des Endes der Lebenszeit der Position verwendeten Variablen position_StartTime und position_EndTime.

// set the initial value for the beginning time of deal
position_StartTime=time_curr;
// set the initial value for the end time of deal
position_EndTime=time_curr;

In den Variablen in_table_volume, in_table_time, in_table_price, out_table_volume, out_table_time, out_table_price, out_table_swap und out_table_profit werden Tabellen angelegt, die sich in den Feldern einer größeren Tabelle wiederfinden: Umfang, Zeit und Kurs bei Markteinstieg sowie Zeit, Kurs, Austausch (Swap) und Gewinn bei Marktausstieg. In die Variable in_table_volume wird auch die Bezeichnung des Kürzels sowie die Verknüpfung mit der der Richtung der eröffneten Position entsprechenden Bilddatei eingetragen. In all diese Variablen werden die Eingangswerte eingetragen.

// creating the string in report - instrument, position direction, beginning of table for volumes to enter the market
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY)
   StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer],
   "</td><td align='center'><img src='buy.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>");

if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
   StringConcatenate(in_table_volume,"<tr><td align='left'>",symb_list[symb_pointer],
   "</td><td align='center'><img src='sell.gif'></td><td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>");
// creating the beginning of time table to enter the market
in_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of price table to enter the market
in_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of volume table to exit the market
out_table_volume="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of time table to exit the market
out_table_time="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of price table to exit the market
out_table_price="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of swap table to exit the market
out_table_swap="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";
// creating the beginning of profit table to exit the market
out_table_profit="<td><table border='1' width='100%' bgcolor='#FFFFFF' bordercolor='#DFDFFF'>";

Beginnend mit dem aktuellen werden alle Abschlüsse ausgelesen, bis die Position geschlossen wird. Wenn sie zuvor noch nicht verarbeitet worden sind, werden sie jetzt verarbeitet.

// process all deals for this position starting with the current(until position is closed)
for(j=i;j<total_deals_number;j++)
  {
   // if the deal has not been processed yet - process it
   if(deal_status[j]<127)
     {

Bei Auswahl eines Abschlusses erhalten wir den entsprechenden Händlerzettel.

// select deal, get ticket
ticket=HistoryDealGetTicket(j);

Entspricht das Kürzel, zu dem der Abschluss erfolgt ist, dem der eröffneten Position, so wird er verarbeitet. Der Zeitpunkt des Abschlusses wird abgerufen. Liegt er außerhalb der Bereichsgrenzen der Haltezeit der Position, wird der Bereich erweitert. Der Umfang des Abschlusses wird abgerufen.

// if the instrument of deal matches the instrument of position, that is processed
if(symb_list[symb_pointer]==HistoryDealGetString(ticket,DEAL_SYMBOL))
  {
   // get the deal time
   time_curr=HistoryDealGetInteger(ticket,DEAL_TIME);
   // If the deal time goes beyond the range of position time
   // - extend position time
   if(time_curr<position_StartTime) position_StartTime=time_curr;
   if(time_curr>position_EndTime) position_EndTime=time_curr;
   // get the volume of deal
   lot_current=HistoryDealGetDouble(ticket,DEAL_VOLUME);

Kauf- und Verkaufsabschlüsse werden getrennt verarbeitet. Zunächst die Kaufabschlüsse.

// if this deal is buy
if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_BUY)
  {

Wenn bereits eine Verkaufsposition eröffnet ist, verschwindet dieser Kaufvorgang vom Markt. Wenn der Umfang dieses Abschlusses jedoch größer ist als der der eröffneten kurzen (short) Position, erfolgt eine Richtungsumkehr. In die „String“-Variablen sind die entsprechenden Werte einzutragen. In das Datenfeld deal_status[] tragen wir den Wert 127 ein, wenn der Abschluss vollständig verarbeitet worden ist, bzw. 1, wenn es sich um eine Richtungsumkehr handelt, und der Abschluss für eine andere Position untersucht werden muss.

// if position is opened for sell - this will be exit from market
if(NormalizeDouble(lots_list[symb_pointer],2)<0)
  {
   // if buy volume is greater than volume of opened short position - then this is in/out
   if(NormalizeDouble(lot_current+lots_list[symb_pointer],2)>0)
     {
      // creating table of volumes to exit the market - indicate only volume of opened short position
      StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>");
      // mark position as partially processed
      deal_status[j]=1;
     }
   else
     {
      // if buy volume is equal or less than volume of opened short position - then this is partial or full close
      // creating the volume table to exit the market
      StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");
      // mark deal as processed
      deal_status[j]=127;
     }

   // creating the time table to exit the market
   StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

   // creating the price table to exit the market
   StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
   (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

   // get the swap of current deal
   current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP);

   // if swap is equal to zero - create empty string of the swap table to exit the market
   if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>");
   // else create the swap string in the swap table to exit the market
   else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>");

   // get the profit of current deal
   current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);

   // if profit is negative (loss) - it is displayed as red in the profit table to exit the market
   if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align=right><SPAN style='COLOR: #EF0000'>",
   DoubleToString(current_profit,2),"</SPAN></td></tr>");
   // else - it is displayed as green
   else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>",
        DoubleToString(current_profit,2),"</SPAN></td></tr>");
  }

Wenn bereits eine lange Position eröffnet wurde, handelt es sich bei dem Kauf aus diesem Abschluss um einen Markteinstieg (einen ersten oder einen ergänzenden). Wenn das diesem Abschluss entsprechende Element des Datenfeldes deal_status[] den Wert 1 hat, liegt eine Richtungsumkehr vor. Wir tragen die entsprechenden Werte in die String-Variablen ein und kennzeichnen den Abschluss als verarbeitet (indem wir in das betreffende Element des Datenfeldes deal_status[] den Wert 127 eintragen).

else
   // if position is opened for buy - this will be the enter to the market
   {
    // if this deal has been already partially processed (in/out)
    if(deal_status[j]==1)
      {
       // create the volume table of entering the market (volume, formed after in/out, is put here)
       StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>");
       // indemnity of volume change, which will be produced (the volume of this deal is already taken into account)
       lots_list[symb_pointer]-=lot_current;
      }
    // if this deal has not been processed yet, create the volume table to enter the market
    else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");

    // creating the time table of entering the market
    StringConcatenate(in_table_time,in_table_time,"<tr><td align center>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

    // creating the price table of entering the market
    StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
    (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

    // mark deal as processed
    deal_status[j]=127;
   }

Wir ändern den Umfang der Position um den Umfang des aktuellen Abschlusses. Ist die Position geschlossen (ihr Umfang gleich Null), befassen wir uns nicht länger mit ihr (wir verlassen den Zyklus mit der Variablen „j“) und suchen nach dem nächsten nicht verarbeiteten Abschluss (in dem Zyklus mit der Variablen „i“).

 // change of position volume by the current instrument, taking into account the volume of current deal
 lots_list[symb_pointer]+=lot_current;
 // if the volume of opened position by the current instrument became equal to zero - position is closed
 if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break;
}

Die Verkaufsabschlüsse werden ähnlich verarbeitet, anschließend verlassen wir den Zyklus mit der Variablen „j“.

       // if this deal is sell
       if(HistoryDealGetInteger(ticket,DEAL_TYPE)==DEAL_TYPE_SELL)
         {
          // if position has been already opened for buy - this will be the exit from market
          if(NormalizeDouble(lots_list[symb_pointer],2)>0)
            {
             // if sell volume is greater than volume of opened long position - then this is in/out
             if(NormalizeDouble(lot_current-lots_list[symb_pointer],2)>0)
               {
                // creating table of volumes to exit the market - indicate only volume of opened long position
                StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lots_list[symb_pointer],2),"</td></tr>");
                // mark position as partially processed
                deal_status[j]=1;
               }
             else
               {
                // if sell volume is equal or greater than volume of opened short position - then this is partial or full close
                // creating the volume table to exit the market
                StringConcatenate(out_table_volume,out_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");
                // mark deal as processed
                deal_status[j]=127;
               }

             // creating the time table to exit the market
             StringConcatenate(out_table_time,out_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

             // creating the price table to exit the market
             StringConcatenate(out_table_price,out_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
             (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

             // get the swap of current deal
             current_swap=HistoryDealGetDouble(ticket,DEAL_SWAP);

             // if swap is equal to zero - create empty string of the swap table to exit the market
             if(NormalizeDouble(current_swap,2)==0) StringConcatenate(out_table_swap,out_table_swap,"<tr></tr>");
             // else create the swap string in the swap table to exit the market
             else StringConcatenate(out_table_swap,out_table_swap,"<tr><td align='right'>",DoubleToString(current_swap,2),"</td></tr>");

             // get the profit of current deal
             current_profit=HistoryDealGetDouble(ticket,DEAL_PROFIT);

             // if profit is negative (loss) - it is displayed as red in the profit table to exit the market
             if(NormalizeDouble(current_profit,2)<0) StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'>
             <SPAN style='COLOR: #EF0000'>",DoubleToString(current_profit,2),"</SPAN></td></tr>");
             // else - it is displayed as green
             else StringConcatenate(out_table_profit,out_table_profit,"<tr><td align='right'><SPAN style='COLOR: #00EF00'>",
                  DoubleToString(current_profit,2),"</SPAN></td></tr>");
            }
          else
            // if position is opened for sell - this will be the enter to the market
            {
             // if this deal has been already partially processed (in/out)
             if(deal_status[j]==1)
               {
                // create the volume table of entering the market (volume, formed after in/out, is put here)
                StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(-lots_list[symb_pointer],2),"</td></tr>");

                // indemnity of volume change, which will be produced (the volume of this deal is already taken into account)
                lots_list[symb_pointer]+=lot_current;
               }
             // if this deal has not been processed yet, create the volume table to enter the market
             else StringConcatenate(in_table_volume,in_table_volume,"<tr><td align='right'>",DoubleToString(lot_current,2),"</td></tr>");

             // creating the time table of entering the market
             StringConcatenate(in_table_time,in_table_time,"<tr><td align='center'>",TimeToString(time_curr,TIME_DATE|TIME_SECONDS),"</td></tr>");

             // creating the price table of entering the market
             StringConcatenate(in_table_price,in_table_price,"<tr><td align='center'>",DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),
             (int)SymbolInfoInteger(symb_list[symb_pointer],SYMBOL_DIGITS)),"</td></tr>");

             // mark deal as processed
             deal_status[j]=127;
            }
          // change of position volume by the current instrument, taking into account the volume of current deal
          lots_list[symb_pointer]-=lot_current;
          // if the volume of opened position by the current instrument became equal to zero - position is closed
          if(NormalizeDouble(lots_list[symb_pointer],2)==0 || deal_status[j]==1) break;
         }
      }
   }
}

Liegt der Zeitpunkt, an dem die Position eröffnet wurde /zumindest teilweise) innerhalb des Berichtszeitraums, wird ein entsprechender Eintrag in der Datei report.html ausgegeben.

// if the position period is in the the report period - the position is printed to report
if(position_EndTime>=StartTime && position_StartTime<=EndTime) FileWrite(file_handle,
in_table_volume,"</table></td>",
in_table_time,"</table></td>",
in_table_price,"</table></td>",
out_table_volume,"</table></td>",
out_table_time,"</table></td>",
out_table_price,"</table></td>",
out_table_swap,"</table></td>",
out_table_profit,"</table></td></tr>");

Eintragen des Kontostandes in die Variable balance_prev. Verlassen des Zyklus‘ mit der Variablen „i“.

   }
 // changing balance
 balance_prev=balance;
}

Beendigung der HTML-Datei (Verknüpfungen zu den Bilddateien, Ende der zentrierten Ausrichtung, Ende des Hauptteils, Ende des HTML-Dokuments). Schließen der Datei report.html.

// create the end of html-file
   FileWrite(file_handle,
         "</table><br><br>"+
            "<h2>Balance Chart</h2><img src='picture1.gif'><br><br><br>"+
            "<h2>Price Chart</h2><img src='picture2.gif'>"+
         "</center>"+
         "</body>"+
   "</html>");
// close file
   FileClose(file_handle);

Festlegung der maximalen Wartezeit bis zur Aktualisierung des Diagramms auf den in der Konstanten „timeout“ festgelegten Wert.

// get current time
time_curr=TimeCurrent();
// waiting for chart update
while(SeriesInfoInteger(Symbol(),Picture1_period,SERIES_BARS_COUNT)==0 && TimeCurrent()-time_curr<timeout) Sleep(1000);

Einstellung der festgelegten Höchst- und Tiefstwerte des Diagramms.

// setting maximal and minimal values for the balance chart (10% indent from upper and lower boundaries)
ChartSetDouble(hChart,CHART_FIXED_MAX,max_val+(max_val-min_val)/10);
ChartSetDouble(hChart,CHART_FIXED_MIN,min_val-(max_val-min_val)/10);

Einstellung der Eigenschaften des Kontoverlaufsdiagramms.

// setting properties of the balance chart
ChartSetInteger(hChart,CHART_MODE,CHART_LINE);                // chart as line
ChartSetInteger(hChart,CHART_FOREGROUND,false);               // chart on foreground
ChartSetInteger(hChart,CHART_SHOW_BID_LINE,false);            // hide BID line
ChartSetInteger(hChart,CHART_COLOR_VOLUME,White);             // volumes and orders levels are white
ChartSetInteger(hChart,CHART_COLOR_STOP_LEVEL,White);         // SL and TP levels are white
ChartSetInteger(hChart,CHART_SHOW_GRID,true);                 // show grid
ChartSetInteger(hChart,CHART_COLOR_GRID,LightGray);           // grid is light-gray
ChartSetInteger(hChart,CHART_SHOW_PERIOD_SEP,false);          // hide period separators
ChartSetInteger(hChart,CHART_SHOW_VOLUMES,CHART_VOLUME_HIDE); // hide volumes
ChartSetInteger(hChart,CHART_COLOR_CHART_LINE,White);         // chart is white
ChartSetInteger(hChart,CHART_SCALE,0);                        // minimal scale
ChartSetInteger(hChart,CHART_SCALEFIX,true);                  // fixed scale on vertical axis
ChartSetInteger(hChart,CHART_SHIFT,false);                    // no chart shift
ChartSetInteger(hChart,CHART_AUTOSCROLL,true);                // autoscroll enabled
ChartSetString(hChart,CHART_COMMENT,"BALANCE");               // comment on chart

Neuzeichnung des Kontoverlaufsdiagramms.

// redraw the balance chart
ChartRedraw(hChart);
Sleep(8000);

Anfertigung einer Bildschirmaufnahme mit dem Diagramm (Speichern als picture1.gif). Die Breite des Diagramms wird an die Breite des Berichtszeitraums angepasst (aber wegen der börsenfreien Tage kommt es des Öfteren zu Ungenauigkeiten, weswegen das Diagramm breiter ausfallen kann als die Kontoverlaufskurve), die Höhe beträgt stets die Hälfte der Breite.

// screen shooting the balance chart
ChartScreenShot(hChart,"picture1.gif",(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period),
(int)(EndTime-StartTime)/PeriodSeconds(Picture1_period)/2,ALIGN_RIGHT);

Entfernen aller Objekte aus dem Diagramm, bevor es geschlossen wird.

// delete all objects from the balance chart
ObjectsDeleteAll(hChart);
// close chart
ChartClose(hChart);

Wenn der Versand von Dateien per FTP zugelassen ist, Versenden der drei Dateien: report.html, picture1.gif und picture2.gif.

// if report publication is enabled - send via FTP
// HTML-file and two images - price chart and balance chart
if(TerminalInfoInteger(TERMINAL_FTP_ENABLED))
   {
    SendFTP("report.html");
    SendFTP("picture1.gif");
    SendFTP("picture2.gif");
   }
}

Damit ist das Programm fertig. Um den Dateiversand per FTP zu ermöglichen, müssen einige Parameter in MetaTrader 5 eingerichtet werden. Öffnen Sie dazu das Menü „Dienste“ (Tools), dort den Bereich „Einstellungen“ (Options) und dann die Registerkarte „Veröffentlichung“ (Publisher) (Abbildung 4).

Abbildung 4. Einstellungen für die Veröffentlichung des Berichts per FTP.

Abbildung 4. Einstellungen für die Veröffentlichung des Berichts per FTP.

In den Einstellungen müssen das Kontrollfeld „Zulassen“ aktiviert und die Kontonummer sowie die Adresse des FTP-Servers, der Pfad zu ihm, die Benutzerkennung und das Zugangspasswort angegeben werden. Die Häufigkeit der Aktualisierungen und die Art des Berichts sind unerheblich.

Das Skript kann jetzt gestartet werden. Nach dem Start erscheint das Kontoverlaufsdiagramm für einige Sekunden auf dem Bildschirm, bevor es wieder verschwindet. In dem Protokoll wird angezeigt, ob Fehler vorliegen oder ob der Dateiversand an den Server via FTP erfolgreich abgeschlossen wurde. Wenn alles gut gegangen ist, werden auf dem Server in dem in den Einstellungen angegebenen Ordner drei neue Dateien angezeigt. Wenn dort zwei weitere Dateien mit Pfeilabbildungen abgelegt werden, und der Webserver entsprechend eingerichtet und funktionsfähig ist, können Sie sich den Bericht in Ihrem Browser anzeigen lassen.


2. Nachrichtenversand als SMS

Es kann vorkommen, dass Sie keinen Zugang zu Ihrem Rechner oder anderen elektronischen Geräten außer Ihrem Mobiltelefon haben. Sie möchten den Handel auf Ihrem Konto oder den Kurswert eines bestimmten Finanzinstrumentes dennoch gerne im Auge behalten. Für solche Fälle kann der Versand von SMS-Nachrichten an mobile Empfangsgeräte eingerichtet werden. Einige Mobilfunkbetreiber (und Fremdfirmen) bieten E-MAIL-SMS-Dienste, die den Empfang von an eine bestimmte E-Mail-Adresse verschickten Textnachrichten ermöglichen.

Für diese Funktion muss ein gültige E-Mail-Konto vorhanden sein (genauer gesagt, ein SMTP-Server). Dazu sind in der MetaTrader 5-Anwendung auf dem Ausgabegerät einige Einstellungen vorzunehmen. Öffnen Sie dazu das Menü „Dienste“ (Tools), dort den Bereich „Einstellungen“ (Options) und dann die Registerkarte „E-Mail“ (Abbildung 5).

Abbildung 5. Einrichtung des Nachrichtenversands per E-Mail

Abbildung 5. Einrichtung des Nachrichtenversands per E-Mail

In den Einstellungen müssen das Kontrollfeld „Zulassen“ aktiviert und die Adresse des SMTP-Servers, die Benutzerkennung und das Passwort sowie die Adressen des Absenders (Ihre eigene E-Mail-Adresse) und des Empfängers (die E-Mail-Adresse, an die die Nachricht als SMS versendet wird. Diese erfahren Sie bei Ihrem Mobilfunkanbieter) angegeben werden. Wenn alle Angaben korrekt sind, können Sie durch Betätigen der Schaltfläche „Test“ den Versand einer Prüfnachricht veranlassen (ausführliche Informationen darüber enthält das Protokoll).

Die einfachste Art der Erstellung einer Benachrichtigung bei Erreichen eines bestimmten Kursniveaus ist das Anlegen eines Signals (Alert). Öffnen Sie dazu die entsprechende Registerkarte im Fenster „Werkzeuge“ (Toolbox), klicken Sie mit der rechten Maustaste darauf und wählen Sie „Anlegen“ (Abbildung 6).

Abbildung 6. Ein Signal anlegen

Abbildung 6. Ein Signal anlegen

In dem sich öffnenden Fenster müssen das Kontrollfeld „Zulassen“ aktiviert, der Vorgang „Meldung“ angegeben, das Kürzel und die Bedingung ausgewählt sowie der Wert für die Bedingung eingegeben und der Mitteilungstext geschrieben werden. In dem Feld „Maximale Wiederholungen“ ist eine „1“ anzugeben, wenn die Mitteilung nicht mehrmals angezeigt werden soll. Wenn alle Felder ausgefüllt sind, klicken Sie auf „OK“.

Beim Versand einer Mitteilung aus einem MQL5-Programm bestehen noch weitere Möglichkeiten. Wir verwenden die Funktion SendMail(). Sie verfügt über zwei Parameter. Der erste ist die Betreffzeile der Nachricht, die zweite ihr Hauptteil.

Die Funktion SendMail() kann entweder nach der Handelsanfrage (der Funktion OrderSend()) oder in der Ereignisverarbeitungsroutine Trade aufgerufen werden. Somit erhalten wir Benachrichtigungen über Handelsereignisse: Markteinstiege, Auftragserteilungen, das Schließen von Positionen. Außerdem kann die Funktion SendMail() auch in der OnTimer()-Funktion untergebracht werden, dann erhalten wir regelmäßig Mitteilungen zur aktuellen Kursentwicklung. Der Mitteilungsversand kann ebenfalls an das Auftreten bestimmter Handelssignale gekoppelt werden, etwa die Überschneidung von Indikatorlinien, das Erreichen bestimmter Kurslinien und -grenzen...

Sehen wir uns einige Beispiele an.

Ersetzt man in dem Expert-System oder dem Skript die Zeile

OrderSend(request,result};

 durch folgenden Programmteil:

string msg_subj,msg_text;
if(OrderSend(request,result))
  {
   switch(request.action)
     {
      case TRADE_ACTION_DEAL:
         switch(request.type)
           {
            case ORDER_TYPE_BUY:
               StringConcatenate(msg_text,"Buy ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL:
               StringConcatenate(msg_text,"Sell ",result.volume," ",request.symbol," at price ",result.price,", SL=",request.sl,", TP=",request.tp);
               break;
           }
         break;
      case TRADE_ACTION_PENDING:
         switch(request.type)
           {
            case ORDER_TYPE_BUY_LIMIT:
               StringConcatenate(msg_text,"Set BuyLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_LIMIT:
               StringConcatenate(msg_text,"Set SellLimit ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_BUY_STOP:
               StringConcatenate(msg_text,"Set BuyStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_STOP:
               StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_BUY_STOP_LIMIT:
               StringConcatenate(msg_text,"Set BuyStopLimit ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit,
               ", SL=",request.sl,", TP=",request.tp);
               break;
            case ORDER_TYPE_SELL_STOP_LIMIT:
               StringConcatenate(msg_text,"Set SellStop ",result.volume," ",request.symbol," at price ",request.price,", stoplimit=",request.stoplimit,
               ", SL=",request.sl,", TP=",request.tp);
               break;
           }
         break;
       case TRADE_ACTION_SLTP:
          StringConcatenate(msg_text,"Modify SL&TP. SL=",request.sl,", TP=",request.tp);
          break;
       case TRADE_ACTION_MODIFY:
          StringConcatenate(msg_text,"Modify Order",result.price,", SL=",request.sl,", TP=",request.tp);
          break;
       case TRADE_ACTION_REMOVE:
          msg_text="Delete Order";
          break;
     }
  }
  else msg_text="Error!";
StringConcatenate(msg_subj,AccountInfoInteger(ACCOUNT_LOGIN),"-",AccountInfoString(ACCOUNT_COMPANY));
SendMail(msg_subj,msg_text);

so verwendet die Funktion OrderSend() nach Ausführung der Handelsanfrage zum Versand der Mitteilung die Funktion SendMail(). Diese beinhaltet die Angabe der Nummer des Handelskontos, des Maklernamens sowie der ausgeführten Operationen (Kauf, Verkauf, Platzierung eines bedingten Auftrags (Pending Order) und die Änderung oder Löschung eines Auftrags), etwa so:

59181-MetaQuotes Software Corp. Buy 0.1 EURUSD at price 1.23809, SL=1.2345, TP=1.2415

Und wenn in einem Expert-System oder Indikator im Hauptteil der Funktion OnInit() mithilfe der Funktion EventSetTimer() ein Zeitgeber gestartet wird, ist deren einziger Parameter die Laufzeit des Zeitgebers in Sekunden):

void OnInit()
  {
   EventSetTimer(3600);
  }

 In der Funktion OnDeinit() darf nicht vergessen werden, sie mithilfe der Funktion EventKillTimer() auszuschalten:

void OnDeinit(const int reason)
  {
   EventKillTimer();
  }

und in der Funktion OnTimer() mittels SendMail() den Mitteilungsversand auszuführen:

void OnTimer()
  {
   SendMail(Symbol(),DoubleToString(SymbolInfoDouble(Symbol(),SYMBOL_BID),_Digits));
  }

anschließend werden in den angegebenen Abständen Mitteilungen mit dem aktuellen Kurs des gewünschten Kürzels eingehen.


Fazit

In diesem Beitrag wurde beschrieben, wie mithilfe eines MQL5-Programms eine HTML-Datei sowie Bilddateien angelegt und wie sie mittels FTP auf einen Webserver geladen werden können. Außerdem wurde gezeigt, wie man den Versand von Benachrichtigungen als SMS-Mitteilungen auf mobile Empfangsgeräte einrichtet.