Wie man nutzerdefinierte MOEX-Symbole in MetaTrader 5 erstellt und testet

7 Februar 2019, 08:30
Dmitrii Troshin
0
117

Einführung

Zu den beiden grundlegenden Arten von Finanzmärkten gehören Börsen- und Freiverkehrsmärkte (Over-The-Counter, OTC). Wir können den OTC-Forex-Handel mit modernen MetaTrader- und MetaEditor-Tools genießen, die ständig weiterentwickelt werden. Zusätzlich zur Handelsautomatisierung ermöglichen diese Tools ein umfassendes Testen von Handelsalgorithmen mit historischen Daten.

Wie sieht es mit der Nutzung eigener Ideen für den Börsenhandel aus? Einige Börsenhandelsterminals verfügen über integrierte Programmiersprachen. So verfügt beispielsweise das beliebte Transaq-Terminal über die Programmiersprache ATF (Advanced Trading Facility). Aber natürlich mann man es nicht mit MQL5 vergleichen. Darüber hinaus verfügt es über keine Strategie-Testfunktionalität. Eine gute Lösung ist die Beschaffung von Börsendaten und die Optimierung von Handelsalgorithmen mit dem Strategie-Tester des MetaTraders.

Dies kann durch die Erstellung von benutzerdefinierten Symbolen erreicht werden. Der Prozess der Erstellung benutzerdefinierter Symbole wird im Artikel Erstellen und Testen benutzerdefinierter Symbole im MetaTrader 5 ausführlich beschrieben. Dazu müssen lediglich Daten im CSV-Format (TXT) beschafft und die Preisentwicklung nach den im Artikel beschriebenen Schritten importiert werden.

Das wäre einfach, wenn sich die Datenformate nicht unterscheiden würden. Lassen Sie uns zum Beispiel die beliebte börsenrelevante Web-Quelle finam.ru betrachten. Angebote können hier heruntergeladen werden:



Export der Kurse der Börse von Moskau


Welche Daten bietet Finam:




Zur Verfügung stehen die Datumsformate: "yyyymmdd", "yymmdd", "ddmmyy", "dd/mm/yy", "mm/dd/yy". Unser Format:



Das Format, das wir benötigen "yyyy.mm.dd" ist nicht verfügbar. So bietet finam.ru eine große Vielfalt an Formaten, aber nicht das, was wir brauchen. 

Außerdem gibt es noch viele andere Börsenplätze. Formate, die von anderen Websites bereitgestellt werden, können ebenfalls unpassend sein. Wir brauchen eine bestimmte Reihenfolge der Daten. Die Kurse könnten jedoch in einer anderen Reihenfolge gespeichert werden, z.B. Open, Close, High, Low. 

Daher ist es unsere Aufgabe, die in zufälliger Reihenfolge und in verschiedenen Formaten bereitgestellten Daten in das gewünschte Format zu konvertieren. Dies bietet die Möglichkeit, Daten für MetaTrader 5 aus beliebigen Quellen zu erhalten. Dann erstellen wir mit den MQL5-Tools ein benutzerdefiniertes Symbol auf Basis der empfangenen Daten, das es uns ermöglicht, die Tests durchzuführen.

Es gibt eine Reihe von Schwierigkeiten im Zusammenhang mit dem Import von Kursen.

Die Börse unterstützt Spread, Ask und Bid. Alle diese Werte existieren jedoch "momentan", in der Markttiefe. Danach wird unabhängig vom Ausführungspreis nur noch der Ausführungspreis geschrieben., d.h. Ask oder Bid. Wir benötigen den Spread-Wert für das Terminal. Hier wird ein fester Spread hinzugefügt, da es unmöglich ist, den Spread der Markttiefe wiederherzustellen. Wenn der Spread unerlässlich ist, können Sie ihn irgendwie simulieren. Eine der Methoden ist im Artikel Modellierung von Zeitreihen unter Verwendung nutzerdefinierter Symbole nach festgelegten Verteilungsgesetzen beschrieben. Alternativ können Sie eine einfache Funktion schreiben, die die Abhängigkeit der Spreads von der Volatilität darstellt Spread = f(High-Low)..

Bei der Arbeit mit Zeitfenstern ist die Verwendung eines festen Spreads durchaus akzeptabel. Der Fehler wird in großen Zeiträumen geringfügig sein. Die Modellierung der Spreads jedoch wichtig für Ticks. Tick-Format der Börse:


Unser Format:



Hier müssen wir zusätzlich zu LAST auch ASK und BID bestimmen. Die Daten werden mit Millisekundengenauigkeit eingetragen. Die Börse bietet nur einen Strom von Preisen. Die Daten der ersten Seite sind eher wie das Teilen eines großen Stückes in Einzelteile. Es gibt keine Ticks, was den Forex betrifft. Dies kann Bid oder Ask oder beides, Bid und Ask gleichzeitig, sein. Darüber hinaus müssen wir die Deals künstlich nach Zeit aufreihen und die Millisekunden hinzufügen.

Der Artikel beschäftigt sich also nicht mit dem Datenimport, sondern mit der Datenmodellierung, genau wie der oben genannte Artikel. Um Sie nicht zu täuschen, habe ich mich daher entschieden, keine Tick-Importanwendung zu veröffentlichen, die nach dem Prinzip ASK=BID(+spread)=LAST arbeitet. Der Spread ist bei der Arbeit mit Millisekunden von Bedeutung, daher müssen wir beim Testen eine geeignete Modellierungsmethode wählen.

Die Änderung des Codes für den Import der Ticks danach dauert einige Minuten. Es ist nur notwendig, die Struktur MqlRates durch die von MqlTick zu ersetzen. Die Funktion CustomRatesUpdate() muss durch CustomTicksAdd() ersetzt werden.

Der nächste Punkt ist mit der Unfähigkeit verbunden, alle möglichen Datenformate zu berücksichtigen. Beispielsweise können Zahlen mit Trennzeichen 1 000 000 000 oder mit einem Dezimalkomma anstelle eines Dezimalpunktes wie 3,14 geschrieben werden. Oder noch schlimmer - wenn sowohl das Datentrennzeichen als auch das Dezimaltrennzeichen ein Punkt oder ein Komma sind (wie unterscheidet man zwischen ihnen). Dabei werden nur die gängigsten Formate berücksichtigt. Wenn Sie es mit einem nicht standardmäßigen Format zu tun haben, müssen Sie selbst eine Lösung programmieren.

Darüber hinaus gibt es keine Historie der Ticks der Börse - sie liefert nur das Volumen der Transaktionen. Deshalb verwenden wir in diesem Artikel das Börsenvolumen =VOL=TICKVOL.

Der Artikel ist in zwei Teile gegliedert. Teil eins enthält die Codebeschreibung. So können Sie sich mit dem Code vertraut zu machen, so dass Sie ihn später für Operationen mit nicht standardisierten Datenformaten bearbeiten können. Teil zwei enthält eine Schritt-für-Schritt-Anleitung (Nutzerhandbuch). Es ist für diejenigen gedacht, die nicht an der Programmierung interessiert sind, sondern nur die implementierte Funktionen nutzen wollen. Wenn Sie mit Standarddatenformaten arbeiten (insbesondere mit denen der Website finam.ru als Quelle), können Sie sofort mit Teil 2 fortfahren.

Teil 1. Beschreibung des Codes

Hier wird nur ein Teil des Codes besprochen. Der vollständige Code ist in der angehängten Datei verfügbar.

Geben Sie zunächst die erforderlichen Parameter ein, wie z.B. die Position der Daten in der Zeichenkette, Dateiparameter, Symbolname, etc.

input int SkipString        =1;                               // Die Anzahl der auszulassenden Zeichenketten
input string mark1          ="Time position and format";      // Zeit
input DATE indate           =yyyymmdd;                        // Datumsformat der Quelle
input TIME intime           =hhdmmdss;                        // Zeitformat der Quelle
input int DatePosition      =1;                               // Position des Datums
input int TimePosition      =2;                               // Position der Zeit
//------------------------------------------------------------------+
input string mark2          ="Price data position";           // Preis
input int OpenPosition      =3;                               // Eröffnungspreis der Position
input int HighPosition      =4;                               // Höchstpreis der Position
input int LowPosiotion      =5;                               // Tiefstpreis der Position
input int ClosePosition     =6;                               // Schlusskurs der Position
input int VolumePosition    =7;                               // Volumen der Position
input string mark3          ="File parameters";               // Datei
//-------------------------------------------------------------------+
input string InFileName     ="sb";                            // Dateiname der Quelle
input DELIMITER Delimiter   =comma;                           // Trennzeichen
input CODE StrType          =ansi;                            // Typ der Zeichenkette
input string mark4          ="Other parameters";              // Anderes
//-------------------------------------------------------------------+
input string spread         ="2";                             // Konstanter Spread in Points
input string Name           ="SberFX";                        // Name des zu erstellenden Symbols


Es werden für einige Daten Enumeration definiert. Zum Beispiel für die Formate für Datum und Zeit:

enum DATE
{
yyyycmmcdd, // yyyy.mm.dd
yyyymmdd,   // yyyymmdd
yymmdd,     // yymmdd
ddmmyy,     // ddmmyy   
ddslmmslyy, // dd/mm/yy
mmslddslyy  // mm/dd/yy
// Weitere Formate müssen hier angegeben werden
};

enum TIME
{
hhmmss,     // hhmmss
hhmm,       // hhmm
hhdmmdss,   // hh:mm:ss
hhdmm       // hh:mm
// Weitere Formate müssen hier angegeben werden
};


Wenn das benötigte Format nicht verfügbar ist, ergänzen Sie es.

Öffnen Sie dafür die Quelldatei. Für die komfortable Bearbeitung von formatierten Daten empfehle ich, diese in einer CSV-Datei zu speichern. Gleichzeitig sollten die Daten in die Struktur MqlRates geschrieben werden, um die automatische Erstellung des benutzerdefinierten Symbols zu ermöglichen.

// Datei öffnen
    
  int out =FileOpen(InFileName,FILE_READ|StrType|FILE_TXT);
  if(out==INVALID_HANDLE)
  {
   Alert("Failed to open the file for reading");
   return; 
  }
// Öffnen der Ausgabedatei
  int in =FileOpen(Name+"(f).csv",FILE_WRITE|FILE_ANSI|FILE_CSV);
  if(in==INVALID_HANDLE)
  {
   Alert("Failed to open the file for writing");
   return; 
  }
  //---Einfügen der Titelzeile  
  string Caption ="<DATE>\t<TIME>\t<OPEN>\t<HIGH>\t<LOW>\t<CLOSE>\t<TICKVOL>\t<VOL>\t<SPREAD>";
  FileWrite(in,Caption);
//-----------------------------------------------------------
string fdate="",ftime="",open="";
string high="",low="",close="",vol="";
int left=0,right=0;

string str="",temp="";
for(int i=0;i<SkipString;i++)
   {
   str =FileReadString(out);
   i++;
   }
MqlRates Rs[];
ArrayResize(Rs,43200,43200);  //43200 Minuten in einem Monat
datetime time =0;


Die Quelldatei muss im Verzeichnis MQL5/Files gespeichert werden. Die externe Variable SkipString stellt die Anzahl der zu überspringenden Zeilen im Dateikopf dar. Um Leerzeichen und Tabulatoren als Trennzeichen verwenden zu können, öffnen wir die Datei mit dem Flag FILE_TXT. 

Dann müssen wir die Daten aus der Zeichenkette extrahieren. Die Position wird in den Eingabeparametern angegeben. Die Nummerierung beginnt bei 1. Lassen Sie uns als Beispiel die Kurse der Sberbank-Aktien verwenden.



Hier ist die Position des Datums 1, der Zeit 2 usw. SkipString=1.

Um die Zeichenkette zu analysieren, könnten wir die Funktion StringSplit() verwenden. Aber es ist besser, eigene Funktionen zu entwickeln, um Fehler in der Quelldatei komfortabler zu überwachen. Zu solchen Funktionen kann die Datenanalyse hinzugefügt werden. Allerdings wäre die Verwendung des StringSplit()-Codes einfacher. Die erste Funktion, die Datengrenzen findet, übernimmt die Zeichenkette, das Trennzeichen und die Position. Die Grenzen werden in die Variablen a und b geschrieben, die per Referenz übergeben werden.

//---Suche nach den Positionsgrenzen der Daten-----------------------+
bool SearchBorders(string str,int pos,int &a,int &b,DELIMITER delim)
{
// Hilfsvariablen
int left=0,right=0;
int count=0;
int start=0;
string delimiter="";
//-------------------------------------------------------------------+

switch(delim)
{
case comma : delimiter =",";
   break; 
case tab : delimiter ="/t";
   break;
case space : delimiter =" ";
   break;
case semicolon : delimiter =";";
   break;
}

while(count!=pos||right!=-1)
   {
   right =StringFind(str,delimiter,start);
      
   if(right==-1&&count==0){Print("Wrong date");return false;} //Falsches Datum
   
   if(right==-1)
      {
      right =StringLen(str)-1;
      a =left;
      b =right;
      break;
      }
   
   count++;
      if(count==pos)
      {
      a =left;
      b =right-1;
      return true;
      }
   left =right+1;
   start =left;
   }

return true;
}


Holen wir uns nun mit StringSubstr() die entsprechenden geeignete. Die empfangenen Werte müssen in das gewünschte Format konvertiert werden. Zu diesem Zweck schreiben wir Datums- und Zeitumwandlungsfunktionen. Hier ist zum Beispiel die Funktion der Datumskonvertierung:

//---Datumsformatierung----------------------------------------------+
//2017.01.02
string DateFormat(string str,DATE date)
{

string res="";
string yy="";
 
switch(date)
  { 
   case yyyycmmcdd :  //Unser Format
      res =str;
      if(StringLen(res)!=10)res=""; // Prüfen des Datumsformats
   case yyyymmdd :
      res =StringSubstr(str,0,4)+"."+StringSubstr(str,4,2)+"."+StringSubstr(str,6,2);
      if(StringLen(res)!=10)res=""; // Prüfen des Datumsformats    
      break;
   case yymmdd :
      yy =StringSubstr(str,0,2);
      if(StringToInteger(yy)>=70)
         yy ="19"+yy;
      else
         yy ="20"+yy;
      res =yy+"."+StringSubstr(str,2,2)+"."+StringSubstr(str,4,2);
      if(StringLen(res)!=10)res=""; // Prüfen des Datumsformats  
      break;
//---Andere Formate (gesamter Code ist in der Datei)-------------
//Analyse anderer Formate falls nötig hinzufügen      
   default :
      break; 

  }

return res;
}


Wenn das gewünschte Format nicht verfügbar ist (z.B. ein Datum wie der 01. Januar 18), muss es hinzugefügt werden. Hier wird geprüft, ob die empfangenen Daten dem gewünschten Format entsprechen (bei einem Fehler in der Quelldatei) if(StringLen(res)!=10) res="";Ich weiß, dass dies keine gründliche Überprüfung ist. Die Datenanalyse ist jedoch keine leichte Aufgabe, so dass für eine detailliertere Analyse ein eigenes Programm erforderlich wäre. Im Fehlerfall gibt die Funktion res =,"" zurück und die entsprechende Zeile wird dann übersprungen.

Für Formate vom Typ ddmmyyyy, bei denen das Jahr zweistellig geschrieben wird, ist folgende Konvertierung vorgesehen. Werte >=70 werden in 19yy umgewandelt, Werte, die kleiner sind als die Werte, werden in 20yy umgewandelt.

Nach der Formatkonvertierung schreiben wir Daten in entsprechende Variablen und erstellen eine endgültige Zeichenkette.

while(!FileIsEnding(out))
   {
   str =FileReadString(out);
   count++;
//---fdate-----------------------------
   if(SearchBorders(str,DatePosition,left,right,Delimiter))
   {
   temp =StringSubstr(str,left,right-left+1);
   fdate =DateFormat(temp,indate);
   if(fdate==""){Print("Error in string   ",count);continue;}
   }
   else {Print("Error in string   ",count);continue;}
//---Andere Daten werden ähnlich behandelt.


Wenn ein Fehler in den Funktionen SearchBorders, DateFormat oder TimeFormat gefunden wird, wird die Zeichenkette übersprungen und ihre Sequenznummer mit der Funktion Print() geschrieben. Alle Funktionen für Aufzählung und Formatkonvertierung befinden sich in der separaten Include-Datei FormatFunctions.mqh. 

Dann wird die resultierende Zeichenkette gebildet und geschrieben. Die Daten werden den entsprechenden Elementen der MqlRates-Struktur zugewiesen.

//-------------------------------------------------------------------+
   str =fdate+","+ftime+","+open+","+high+","+low+","+close+","+vol+","+vol+","+Spread;
   FileWrite(in,str);
//---Zuweisung MqlRates -----------------------------------------------+
   Rs[i].time               =time;
   Rs[i].open               =StringToDouble(open);
   Rs[i].high               =StringToDouble(high);
   Rs[i].low                =StringToDouble(low);
   Rs[i].close              =StringToDouble(close);
   Rs[i].real_volume        =StringToInteger(vol);
   Rs[i].tick_volume        =StringToInteger(vol);
   Rs[i].spread             =int(StringToInteger(Spread));
   i++;
//-------------------------------------------------------------------+   
   }


Nachdem Zeichenketten gelesen wurden, erhält das dynamische Array seine endgültige Größe und die Dateien werden geschlossen.

   ArrayResize(Rs,i);
   FileClose(out);
   FileClose(in);

Jetzt ist alles bereit, um ein benutzerdefiniertes Symbol zu erstellen. Zusätzlich haben wir eine CSV-Datei, die direkt im MetaEditor bearbeitet werden kann. Basierend auf der CSV-Datei können wir benutzerdefinierte Symbole mit Standardmethoden im MetaTrader 5 Terminal erstellen.



Erstellen eines benutzerdefinierten Symbols mit MQL5

Nachdem nun alle Daten vorbereitet sind, müssen wir nur noch das benutzerdefinierte Symbol hinzufügen.

   CustomSymbolCreate(Name);
   CustomRatesUpdate(Name,Rs);

Die Kurse werden mit der Funktion CustomRatesUpdate() importiert, so dass das Programm nicht nur zur Symbolerstellung, sondern auch zum Hinzufügen neuer Daten verwendet werden kann. Wenn das Symbol bereits existiert, gibt CustomSymbolCreate() -1 (minus eins) zurück und die Programmausführung wird fortgesetzt, so dass die Kurse durch die Funktion CustomRatesUpdate() aktualisiert werden. Das Symbol wird im Fenster MarketWatch angezeigt und ist grün hinterlegt.



Jetzt können wir den Chart öffnen, um zu prüfen, ob alles richtig funktioniert:


Der Chart EURUSD


Festlegen der Einstellungen (Symboleigenschaften)

Wenn wir ein Symbol testen, müssen wir möglicherweise seine Eigenschaften (Spezifikationen) konfigurieren. Ich habe eine separate Include-Datei für die Einstellungen geschrieben, die eine komfortable Bearbeitung der Symboleigenschaften ermöglicht. In dieser Datei werden die Symboleigenschaften von der Funktion SetSpecifications() festgelegt. Alle Symboleigenschaften der Enumerationen ENUM_SYMBOL_INFO_INTEGER, ENUM_SYMBOL_INFO_DOUBLE, ENUM_SYMBOL_INFO_STRING sind hier zusammengefasst.

void SetSpecifications(string Name)
   {  
//---ganzzahlige Eigenschaften------------------------------
//   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // bool, ein Indikator, dass das Symbol nutzerdefinierte ist
//   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // Farbe, Die Hintergrundfarbe des Symbols im Market Watch
// Andere ganzzahlige Eigenschaften
//---rationalzahlige Eigenschaften----------------------------------------   
//   CustomSymbolSetDouble(Name,SYMBOL_BID,0);                              // Bid, der beste Verkaufspreis des Symbols 
//  CustomSymbolSetDouble(Name,SYMBOL_BIDHIGH,0);                           // höchstes Bid des Tages
//   CustomSymbolSetDouble(Name,SYMBOL_BIDLOW,0);                           // tiefstes Bid des Tages
// Andere rationalzahlige Eigenschaften
//---Eigenschaften der Zeichenketten---------------------------------+
//   CustomSymbolSetString(Name,SYMBOL_BASIS,"");                           // Name des Basiswertes des nutzerdefinierten Symbols
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_BASE,"");                   // Basiswährung des Symbols
//   CustomSymbolSetString(Name,SYMBOL_CURRENCY_PROFIT,"");                 // Währung des Gewinns
// Andere Eigenschaften der Zeichenketten
}


Diese Funktion wird nach der Funktion CustomSymbolCreate ausgeführt. Es ist nicht im Voraus bekannt, welche Art von Symbol dies ist, Futures, Aktien oder Optionen, die meisten Eigenschaften werden nicht benötigt und auskommentiert. Nur einige der Zeilen sind im Quellcode nicht kommentiert:

   CustomSymbolSetInteger(Name,SYMBOL_CUSTOM,true);                       // bool, ein Indikator, dass das Symbol nutzerdefinierte ist
   CustomSymbolSetInteger(Name,SYMBOL_BACKGROUND_COLOR,clrGreen);         // Farbe, die Hintergrundfarbe des Symbols in der Marktübersicht
   CustomSymbolSetInteger(Name,SYMBOL_SELECT,true);                       // bool, ein Indikator, das das Symbol in der Marktübersicht ausgewählt wurde
   CustomSymbolSetInteger(Name,SYMBOL_VISIBLE,true);                      // bool, ein Indikator, dass das Symbol in der Marktübersicht angezeigt wird


Für Testzwecke sind folgende Parameter unkommentiert: minimum volume, volume step, price step, point size, die wichtigsten Eigenschaften. Diese sind typisch für die Aktien der Sberbank. Die Menge der Eigenschaften und deren Eigenschaften unterscheiden sich bei verschiedenen Symbolen.

   CustomSymbolSetDouble(name,SYMBOL_POINT,0.01);               // Der Wert eines Points
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_MIN,1);             // Mindestvolumen eines Deals
   CustomSymbolSetDouble(name,SYMBOL_VOLUME_STEP,1);            // Mindestvolumen einer Volumensänderung
   CustomSymbolSetInteger(name,SYMBOL_DIGITS,2);                // int, Anzahl der Dezimalstellen
   CustomSymbolSetInteger(name,SYMBOL_SPREAD,2);                // int, Spread in Points
   CustomSymbolSetInteger(name,SYMBOL_SPREAD_FLOAT,false);      // bool, ein Indikator für variablen Spreads
   CustomSymbolSetDouble(name,SYMBOL_TRADE_TICK_SIZE,0.01);	// kleinste Preisänderung


Dies wäre ein guter Ansatz, wenn wir den Code nicht jedes Mal neu kompilieren müssten, wenn wir die gewünschten Eigenschaften einstellen müssten. Es wäre bequemer, wenn dies einfach durch Eingabe der gewünschten Parameter geschehen könnte. Deshalb musste ich den Ansatz ändern. Die Symboleigenschaften werden in einer Klartextdatei Specifications.txt bereitgestellt, die für jedes neue Symbol manuell bearbeitet werden kann. Dies erfordert keine Neukompilierung des Quellcodes.

Es ist bequemer, die Textdatei im MetaEditor zu bearbeiten. Vor allem, weil MetaEditor die Hervorhebung von Parametern und Daten ermöglicht. Die Eigenschaften sind im folgenden Format geschrieben:




Die Daten werden durch Kommata getrennt. Die Zeichenketten werden wir folgt analysiert:

   while(!FileIsEnding(handle))
     {
     str =FileReadString(handle);
//--- auszulassende Zeilen------------------+     
     if(str=="") continue;
     if(StringFind(str,"//")<10) continue;
//------------------------------------------+     
     sub =StringSplit(str,u_sep,split);
     if(sub<2) continue;
     SetProperties(SName,split[0],split[1]);
     }


Eine Zeile wird übersprungen, wenn sie leer ist, oder, wenn am Anfang das Kommentarsymbol "//" steht (Position<10). Dann wird die Zeichenkette mit der Funktion StringSplit() in Teilzeichenketten unterteilt. Danach werden die Zeichenketten an die Funktion SetProperties() übergeben, wo die Symboleigenschaften festgelegt werden. Die Struktur des Funktionscodes:

void SetProperties(string name,string str1,string str2)
   {
   int n =StringTrimLeft(str1);
       n =StringTrimRight(str1);
       n =StringTrimLeft(str2);
       n =StringTrimRight(str2);
       
   if(str1=="SYMBOL_CUSTOM")
      {
      if(str2=="0"||str2=="false"){CustomSymbolSetInteger(name,SYMBOL_CUSTOM,false);}
      else {CustomSymbolSetInteger(name,SYMBOL_CUSTOM,true);}
      return;
      }
   if(str1=="SYMBOL_BACKGROUND_COLOR")
      {
      CustomSymbolSetInteger(name,SYMBOL_BACKGROUND_COLOR,StringToInteger(str2));
      return;
      }
   if(str1=="SYMBOL_CHART_MODE")
      {
      if(str2=="SYMBOL_CHART_MODE_BID"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_BID);}
      if(str2=="SYMBOL_CHART_MODE_LAST"){CustomSymbolSetInteger(name,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_LAST);}
      return;
      }
//--- Eigenschaften andrer Symbole
}


Zwei weitere Funktionen wurden für die Fälle hinzugefügt, in denen der Benutzer beim Bearbeiten Leerzeichen oder Tabs hinterlässt, StringTrimLeft() und StringTrimRight()..

Der vollständige Code ist in der Include-Datei PropertiesSet.mqh verfügbar.

Nun werden alle Symboleigenschaften über die angehängte Textdatei festgelegt, es ist keine Neukompilierung mehr nötig. Sie können beide Codevarianten, die unten angehängt sind, überprüfen. Die erste Variante, die das Einstellen der Eigenschaften über eine Include-Datei erfordert, wird auskommentiert.


Schnittstelle

Normalerweise werden die Einstellungen über Eingabeparameter festgelegt. Wenn es nichts zu bearbeiten gibt, können wir über die Schnittstelle nachdenken. Für die endgültige Version habe ich dieses Eingabefeld entwickelt:


Über den Code des Panels. Der Standardsatz der Steuerelemente aus den folgenden Include-Dateien wird hier verwendet:

#include <Controls\Dialog.mqh>
#include <Controls\Label.mqh>
#include <Controls\Button.mqh>
#include <Controls\ComboBox.mqh>

Für die OK-Taste wurde eine Ereignisbehandlung erstellt.

//+------------------------------------------------------------------+ 
//| Ereignisbehandlung                                               | 
//+------------------------------------------------------------------+ 
EVENT_MAP_BEGIN(CFormatPanel) 
ON_EVENT(ON_CLICK,BOK,OnClickButton) 
EVENT_MAP_END(CAppDialog)

void CFormatPanel::OnClickButton(void) 
  { 
// das Programm, wie oben beschrieben
  } 

Jetzt ist fast der gesamte Code des oben beschriebenen Programms in die Ereignisbehandlung gewandert. Die externen Parameter werden zu lokalen Variablen.

long SkipString        =1;                              // Anzahl der auszulassenden Zeichenketten
DATE indate           =yyyymmdd;                        // Datumsformat der Quelle
TIME intime           =hhdmmdss;                        // Zeitformat der Quelle
int DatePosition      =1;                               // Position des Datums
int TimePosition      =2;                               // Position der Zeit
// Andere Parameter

Die Funktion Create() wird für jedes Steuerelement geschrieben, so dass nach seiner Ausführung die entsprechenden Werte zur Liste der Steuerelemente hinzugefügt werden. So wird beispielsweise für das Datumsformat Folgendes durchgeführt:

//-----------ComboBox Datumsformat-----------------------------------+     
    if(!CreateComboBox(CDateFormat,"ComDateFormat",x0,y0+h+1,x0+w,y0+2*h+1))
     {
      return false;
     }
   CDateFormat.ListViewItems(6);
   CDateFormat.AddItem(" yyyy.mm.dd",0);
   CDateFormat.AddItem(" yyyymmdd",1);
   CDateFormat.AddItem(" yymmdd",2);
   CDateFormat.AddItem(" ddmmyy",3);
   CDateFormat.AddItem(" dd/mm/yy",4);
   CDateFormat.AddItem(" mm/dd/yy",5);
   CDateFormat.Select(1);
     }

Diese Werte werden dann von den Eingabefeldern an die korrespondierenden Variablen übergeben:

long sw;  
SkipString =StringToInteger(ESkip.Text());

sw =CDateFormat.Value();
switch(int(sw))
{
   case 0 :indate =yyyycmmcdd;
      break;
   case 1 :indate =yyyymmdd;
      break;
   case 2 :indate =yymmdd;
      break;
   case 3 :indate =ddmmyy;
      break;
   case 4 :indate =ddslmmslyy;
      break;
   case 5 :indate =mmslddslyy;
      break;                
}
// Andere Variablen

 

Diese Version benötigt eine größere Implementierung. Wenn Sie also den Code bearbeiten müssen, sollten Sie mit der Eingabeversion arbeiten.


Teil 2. Schritt-für-Schritt-Anleitung

Dieser Teil enthält eine Schritt-für-Schritt-Anleitung der Aktionen, die für die Erstellung eines benutzerdefinierten Börsensymbols erforderlich sind. Diese Anleitung kann verwendet werden, wenn verfügbare Angebote eines der Standardformate haben und Sie den Code nicht bearbeiten müssen. Zum Beispiel, wenn die Angebote von der Website finam.ru Website erhalten werden. Wenn die Kurse in einigen nicht standardisierten Formaten vorliegen, sollten Sie den in Teil 1 beschriebenen Code bearbeiten.

Wir haben also eine Quelldatei mit den Börsenkursen eines Finanzinstruments. Angenommen, wir haben sie von der Finam-Seite bezogen, wie am Anfang des Artikels beschrieben. Vergessen wir nicht, dass wir die Angebote des einminütigen Zeitrahmens benötigen.

Zwei Möglichkeiten des Datenimports werden im Artikel beschrieben. Sie können entweder das Skript CreateCustomSymbol oder den Expert Advisor CreateSymbolPanel mit dem Panel der Eingabeparameter verwenden. Beide EAs arbeiten genau gleich. Betrachten wir zum Beispiel den Betrieb mit dem Eingabefeld. In den hier aufgeführten Beispielen verwenden wir Aktienkurse der Sberbank der Moskauer Börse. Die Kurse sind unten in der Datei sb.csv angehängt.

1. Dateizuordnung

Zuerst müssen wir die Kursdatei in MQL5/Files speichern. Dies verlangt das MQL5-Programmierkonzept, durch das Operationen mit Dateien aus Sicherheitsgründen streng kontrolliert werden. Die einfachste Methode, das benötigte Verzeichnis zu finden, ist, es aus MetaTrader heraus zu öffnen. Klicken Sie im Navigatorfenster mit der rechten Maustaste in den Ordner Files und wählen Sie aus dem Kontextmenü "Datei öffnen".



Die Quelldatei sollte in diesem Ordner gespeichert werden (der Speicherort der Programmdateien ist im Kapitel Dateien unten beschrieben). Die Datei kann nun im MetaEditor geöffnet werden.



Speichern Sie die Datei Specifications.txt im gleichen Ordner. Es legt die Eigenschaften des Symbols fest.

2. Eingaben

Der nächste Schritt besteht darin, Datenformat und -position festzulegen, Dateieigenschaften auszuwählen und den Namen für unser benutzerdefiniertes Symbol festzulegen. Das Beispiel, wie die Felder ausgefüllt werden können, ist im Folgenden dargestellt:



Die Daten sollten dem Panel übergeben werden. In dieser Version wird ein konstanter Spread in Punkten verwendet, so dass ein variabler Spread nicht modelliert wird. Daher sollten Sie hier den entsprechenden Wert des Spread eingeben.



Schreiben Sie den vollständigen Dateinamen inklusive der Erweiterung.

Geben Sie nun, bevor Sie auf "OK" klicken, die erforderlichen Symboleigenschaften ein. Sie sind in der Datei Specifications.txt verfügbar, die zuvor in MQL5/Files gespeichert wurde.

Es ist sehr praktisch, die Textdatei im MetaEditor zu bearbeiten. Der Hauptgrund ist die im MetaEditor unterstützte Datenhervorhebung. Wenn Sie eine der Eigenschaften nicht verstehen können, bewegen Sie den Mauszeiger über sie und drücken Sie F1.



Die Eigenschaften sind rot markiert und deren Werte grün. Die nicht verwendeten kommentierten Eigenschaften (//) werden grau dargestellt. Beachten Sie, dass zur Datentrennung Kommata verwendet werden. Löschen Sie keine Eigenschaften während der Bearbeitung. Um Fehler zu vermeiden, sollten Sie das vorhandene Format beibehalten.

Um Eigenschaften zu bearbeiten, entfernen Sie deren "//" und legen Sie dann den entsprechenden Wert fest. Ein minimaler Satz von Eigenschaften, der in der angehängten Datei angegeben ist: Preisschritt, Punktwert, Mindestlos, etc.

Alle diese Merkmale (in der Quelldatei) sind für die Aktien der Sberbank an der Moskauer Börse erforderlich. Für andere Finanzinstrumente sind unterschiedliche Eigenschaften erforderlich, daher müssen Sie die Eigenschaften bearbeiten können. 

Der minimale erforderliche Satz von Eigenschaften befindet sich ganz am Anfang der Datei.

Normalerweise haben Aktienkurse 2 Dezimalstellen (SYMBOL_DIGITS), während der Wert eines Punktes gleich 0,01 Rubel ist. Die Anzahl der Dezimalstellen in Aktien-Futures-Preisen ist 0 und der Punktwert ist 1 Rubel. Siehe Spezifikationen unter moex.com.

Nachdem Sie alle erforderlichen Eigenschaften eingestellt haben, klicken Sie auf OK. Das erstellte, benutzerdefinierte Symbol wird jetzt im Navigatorfenster angezeigt. In meinem Beispiel ist es grün markiert.


Wir öffnen den Chart zur Überprüfung:



Alles ist in Ordnung, so dass das benutzerdefinierte Symbol nun im Strategie-Tester getestet werden kann.

Die Einstellung der benutzerdefinierten Symbole erfolgt analog zu einem Standardsymbol. Ein wichtiger Punkt ist hier die richtige Konfiguration der Symbolspezifikationen.

Lassen Sie uns zum Beispiel jeden im Terminal verfügbaren Standard-Expertenberater (hier den Moving Average) mit unseren Daten testen:

 


Alles funktioniert wie erwartet. Wenn Sie neue Angebote hinzufügen oder die Eigenschaften ändern müssen, wiederholen Sie einfach die beschriebenen Aktionen für das bereits vorhandene Symbol. Wenn sich die Spezifikationen nicht geändert haben, klicken Sie auf OK, ohne die Eigenschaften zu bearbeiten.


Dateien

Die angehängten Dateien befinden sich in Ordnern, wie sie auf Ihrem Computer gespeichert werden sollen:

  • CreateCustomSymbol Skript und Code erstellen: MQL5\Scripts
  • CreateSymbolPanel Expert Advisor und Code: MQL5\Experts
  • Include-Dateien FormatFunctions, PropertiesSet, Specification: MQL5\Include
  • Textdatei mit Symboleinstellungen: in MQL5\Files


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/5303

Beigefügte Dateien |
MQL5.zip (526.52 KB)
PropertiesSet.mqh (35.55 KB)
Specification.mqh (21.21 KB)
Specifications.txt (8.04 KB)
Die Wahrscheinlichkeitstheorie für den Handel von Kurslücken verwenden Die Wahrscheinlichkeitstheorie für den Handel von Kurslücken verwenden

In diesem Artikel werden wir die Wahrscheinlichkeitstheorie und die mathematischen Methoden der Statistik für das Erstellen und Testen von Handelsstrategien anwenden. Wir werden auch nach einem optimalen Handelsrisiko suchen, indem wir die Unterschiede zwischen dem Preis und dem Random Walk nutzen. Es ist bewiesen, dass, wenn sich die Preise wie ein Random Walk mit Null-Drift verhalten (ohne Richtungswechsel), ein profitabler Handel unmöglich ist.

Entwicklung eines Symbolauswahl- und Navigationsprogramms in MQL5 und MQL4 Entwicklung eines Symbolauswahl- und Navigationsprogramms in MQL5 und MQL4

Erfahrene Händler sind sich der Tatsache bewusst, dass die meisten zeitaufwendigen Dinge im Handel nicht das Öffnen und Verfolgen von Positionen sind, sondern das Auswählen von Symbolen und das Suchen von Einstiegspunkten. In diesem Artikel werden wir einen EA entwickeln, das die Suche nach Einstiegspunkten für Handelsinstrumente Ihres Brokers vereinfacht.

Hilfen zur Auswahl und Navigation in MQL5 und MQL4: Tabs für "Hausaufgaben" und das Sichern grafischer Objekte Hilfen zur Auswahl und Navigation in MQL5 und MQL4: Tabs für "Hausaufgaben" und das Sichern grafischer Objekte

In diesem Artikel werden wir die Fähigkeiten des zuvor erstellten Hilfsprogramms erweitern, indem wir Tabs (Registerkarten) zur Auswahl der benötigten Symbole hinzufügen. Wir werden auch lernen, wie man grafische Objekte, die wir erstellt haben, auf dem spezifischen Symboldiagramm speichert, damit wir sie nicht ständig neu erstellen müssen. Außerdem erfahren wir, wie man nur mit Symbolen arbeitet, die zuvor über eine bestimmte Website ausgewählt wurden.

Separates Optimieren von Trend- und Seitwärtsstrategie Separates Optimieren von Trend- und Seitwärtsstrategie

Der Artikel betrachtet das separate Optimieren unter verschiedenen Marktbedingungen. Separates Optimieren bedeutet, die optimalen Parameter des Handelssystems zu definieren, indem man für einen Aufwärtstrend und einen Abwärtstrend getrennt optimiert. Um die Wirkung von Fehlsignalen zu reduzieren und die Rentabilität zu verbessern, werden die Systeme flexibel gestaltet, d.h. sie verfügen über einen bestimmten Satz von Einstellungen oder Eingangsdaten, was gerechtfertigt ist, da sich das Marktverhalten ständig ändert.