«

»

Dec 03

MetaTrader Tool zum Loggen der Trade-Historie in txt oder csv

MetaTrader_History_Logging_in_File

Für das MetaTrader Tool welches ich Ihnen in diesem Beitrag vorstellen möchte, haben wir in den letzten Wochen viele Anfragen bekommen: Ein MT-Tool, das in der Lage ist, alle Trades aus der Historie auszulesen und in eine Datei auf der Festplatte zu schreiben, so dass dann in anderen Programmen weitergehende Auswertungen erstellt werden können.
Ähnlich wie schon in den letzten Beiträgen erwähnt, ist MetaTrader ein tolles Tool für das eigentliche Trading. Die Post-Trade-Betrachtungs- und Auswertungsmöglichkeiten sind aber ziemlich bescheiden.

Dieses Tool ließt alle geschlossenen Trades aus und schreibt folgende Daten je Trade Zeile für Zeilen wahlweise in eine csv oder eine txt Datei

#define  _ticketNumber     0
#define  _symbol           1
#define  _openPrice        2
#define  _volume           3
#define  _openTime         4
#define  _closeTime        5
#define  _durationInSec    6
#define  _openDay          7
#define  _closeDay         8
#define  _openMonth        9
#define  _closeMonth       10
#define  _openYear         11
#define  _closeYear        12
#define  _orderCommission  13
#define  _orderSwap        14
#define  _orderProfit      15
#define  _stoploss         16
#define  _takeprofit       17
#define  _magicNumber      18
#define  _orderComment     19
#define  _slippage         20
#define  _cmd              21
#define  _durationInMin    22

#define  _parameterArrayPlaces 23

string allTradesInHistory[1][_parameterArrayPlaces];

… eine ganze Menge Informationen, die natürlich auch noch erweiterbar sind. Für den Anfang sollte das aber die so ziemlich alles abdecken, was wir in den letzten Wochen an Anfragen hatten.

Wie immer ist der Zweck unserer Blogeinträge nicht einfach nur die Erstellung von Tools, die man sich runter laden und frei verwenden kann, sondern, wir möchten den MQL-Entwicklern, die selber ein wenig der Materie näher kommen möchten, eine Hilfestellung geben. Ich werde also im Folgenden wieder kurz auf die wesentlichen Elemente dieser Applikation eingehen und am Ende den MQL Quellcode zur Verfügung stellen.

Step 1: Verteilung der Aufgaben in der OnInit()

Wie auch fast alle anderen Auswertungstools die wir bisher vorgestellt haben, braucht auch dieses Tool keine Startfunktion. Es wird bei Aktivierung einmal durch die OnInit() Funktion gelaufen und danach ist das Programm beendet. Aus der OnInit() werden die zentralen Aufgaben wieder auf die Unterbereiche verteilt.
Jede einzelne Funktion muss erfolgreich durchlaufen sein, damit das Tool nicht vorzeitig mit einer Alarmmeldung abbricht. Wenn es zum Beispiel nicht möglich ist, eine Datei anzulegen, macht das ganze weitere Programm keinen Sinn und es kann abgebrochen werden. Das selbe gilt natürlich für die Auswertung der Trade-Historie und auch für das Schreiben der Daten in die Datei. Die OnInit() Funktion sieht daher wie folgt aus:

void OnInit()
{
   f_showSomeInfosOnTheChart(firstComments);

   if (f_createNewFile() <= 0)
   {
      Alert("Critical Error @ f_createNewFile ... STOP THAT TOOL");
      return;
   }

   if (f_createTradeHistory() < 0)
   {
      Alert("Critical Error @ f_createTradeHistory ... STOP THAT TOOL");
      return;
   }

   if (f_writeHeader() == 0)
   {
      Alert("Critical Error @ f_writeHeader ... STOP THAT TOOL");
      return;
   }

   if (f_writeHistoryIntoFile() == 0)
   {
      Alert("Critical Error @ f_writeHistoryIntoFile ... STOP THAT TOOL");
      return;
   }

   f_closeFile();

   f_showSomeInfosOnTheChart(lastComments);
}

Step 2: Erzeugung der Datei in welche die Historie geschrieben wird

Je nachdem in welchem Programm man weitere Auswertungen erstellen möchte, ist es vorteilhaft eine txt-Datei oder eben eine csv-Datei zu verwenden. Grundsätzlich sind beide Datenformate natürlich sehr ähnlich, aber in gewissen Bereichen spart man sich beim Arbeiten mit csv oder txt einige Arbeitsschritte.
Über eine externe Variable kann man die Auswahl treffen welches Dateiformat man gerne haben möchte

enum ENUM_DATA_FORMAT {_file_as_txt, _file_as_csv};
extern ENUM_DATA_FORMAT fileDataFormat = _file_as_txt;

… das könnte man außerdem auf Binär-Dateien erweitern falls man dieses Datenformat benötigt. Allerdings muss man dann das Schreiben der Daten etwas umbauen, denn das funktioniert anders als bei csv und txt files.

Je nachdem welches Format man gewählt hat, wird in der Funktion f_createNewFile() nun entsprechend der Name-String der neuen Datei angelegt. Man könnte das auch anders lösen indem man über die Flags von FileOpen geht, aber ich finde es komfortabler es einfach genau so hinzuschreiben, wie es sein soll.
Um die Dateien in einem separten Ordner zu sammeln, habe ich außerdem einen Unterordner in logs angelegt, der TradeHistoryLogs heißt.

Der Part
logs\\TradeHistoryLogs\\

sorgt also dafür, dass die Datei unter metatrader/files/logs/TradeHistoryLogs/*.txt abgelegt wird.

FileOpen habe ich so angelegt, dass ich nur die Flag FILE_WRITE verwende. Egal ob die Datei schon vorhanden ist, oder nicht, es soll auf jeden Fall alles was eventuell schon vorhanden ist überschrieben werden. Die Auswertung der MT4 / MT5 Trades erstellt man ja nur einmal, bzw. ab und zu mal neu. Aber dabei wird dann nichts fortgesetzt, sondern es wird einfach alles neu eingelesen und heraus geschrieben.
Hier nun die Funktion f_createNewFile()

int f_createNewFile()
{
   string fName = StringConcatenate("void f_createNewFile()");
   // ------------------------------------------------------------------------------------------------

   string fileName = "";

   if (fileDataFormat == _file_as_txt)
      fileName = StringConcatenate("logs\\TradeHistoryLogs\\Account_",(string)AccountNumber(),"_Broker_",AccountCompany(),".txt");
   else
      if (fileDataFormat == _file_as_csv)
         fileName = StringConcatenate("logs\\TradeHistoryLogs\\Account_",(string)AccountNumber(),"_Broker_",AccountCompany(),".csv");

   fileHandle = FileOpen(fileName, FILE_WRITE);
   if (fileHandle <= 0)
      f_PrintMeTheErrorThatHasOccured(GetLastError(), StringConcatenate(fName,"_fileName:",fileName));

   return(fileHandle);
}

Step 3: Auslesen der Daten aus der Historie in das allTradesInHistory-Array

Wie bereits am Anfang des Artikels zu sehen, habe ich ein zweidimensionales Array namens allTradesInHistory angelegt, in das ich alle Daten für jeden Trade fülle. Dabei werden aber nicht nur einfach die auslesbaren Daten ausgelesen und gespeichert, sondern es werden bereits weitere Berechnungen durchgeführt, die später viel Arbeit sparen, weil man nicht in Excel, Access, oder R-Studio anfangen muss neu zu rechnen. Ich habe unter anderem die Dauer jedes Trades in Sekunden und in Minuten direkt berechnet.
Viel interessanter ist aber sicherlich die Berechnung der Slippage pro Trade!
Insofern es möglich war, also bei allen Trades die einen Stoploss*, oder einen Takeprofit* erreicht haben, habe ich die Slippage berechnet . Ausgenommen davon sind nur Trades, die am Sonntag geschlossen wurden … also alle Trades, die über das Wochenende offen waren und dann mit relativer Sicherheit in ein Weekend-Gap gelaufen sind. Hier liegt die Ursache für Slippage nicht in fehlender Liquidität oder Performance des Brokers, sondern eventuell an semi-cleverem Tradingverhalten des Traders …
Es gibt natürlich auch Trades, die wurden Sonntag eröffnet und wieder geschlossen, diese Variante habe ich aktuell nicht eingebaut. Falls jemand sehr oft am Sonntag tradet, dann sollte diese Variante ergänzt werden, denn dann ist es natürlich interessant, zu wissen, wie sich die Slippage am Sonntag verhält.
Für Auswertungszwecke auch immer sehr interessant ist das Thema “Trading-Tag”. Wir bei ForexInnovation kennen es aus eigener Erfahrung, dass Freitag in der (gefühlten) Regel eher nicht so unser bester Tag ist. Ich habe also Wochentag / Monat / Jahr der Trade-Eröffnung und der Trade-Schließung separat ausgewiesen. Was man nun noch erweitern könnte, wäre die Stunde der Eröffnung und Schließung.

Nachfolgend die zentrale Funktion in der durch alle geschlossenen Trades geloopt wird. Für jeden Trade wird geschaut, ob das Array erweitert werden muss und es wird gleich der nächste zu nutzende Arrayplatz ermittelt. In der Funktion f_fillAllDataInArray() wird dann anschließend das Rechnen und Einfüllen vorgenommen.

int f_createTradeHistory()
{
   string fName = StringConcatenate("void f_createTradeHistory()");
   // ------------------------------------------------------------------------------------------------

   int newPlaceForThisTicket = 0;

   for (int thisClosedTradeByIndex = 0; thisClosedTradeByIndex < OrdersHistoryTotal(); thisClosedTradeByIndex++)
   {
      if (!f_selectThisTrade(thisClosedTradeByIndex, fName, SELECT_BY_POS, MODE_HISTORY))
         continue;

      if (OrderType() != OP_BUY && OrderType() != OP_SELL)
         continue;

      f_resizeArrayIfNeededAndGetNewPlaceForTicket(newPlaceForThisTicket);
      if (newPlaceForThisTicket >= 0)
      {
         totalTradesFoundAndSavedInArray++;
         f_fillAllDataInArray(newPlaceForThisTicket);
      }
      else
         break;
   }

   return(newPlaceForThisTicket);
}

… das Einfüllen der Daten sieht dann wie folgt aus …

void f_fillAllDataInArray(int placeToBeUsed)
{
   allTradesInHistory[placeToBeUsed][_ticketNumber] = (string)OrderTicket();
   allTradesInHistory[placeToBeUsed][_symbol] = OrderSymbol();
   allTradesInHistory[placeToBeUsed][_openPrice] = (string)OrderOpenPrice();
   allTradesInHistory[placeToBeUsed][_volume] = (string)OrderLots();
   allTradesInHistory[placeToBeUsed][_openTime] = (string)OrderOpenTime();
   allTradesInHistory[placeToBeUsed][_closeTime] = (string)OrderCloseTime();
   allTradesInHistory[placeToBeUsed][_durationInSec] = (string)((uint)(OrderCloseTime()) - (uint)(OrderOpenTime()));
   allTradesInHistory[placeToBeUsed][_durationInMin] = (string)( ((uint)(OrderCloseTime()) - (uint)(OrderOpenTime())) / 60 );
   allTradesInHistory[placeToBeUsed][_openDay] = f_convertStaticToDayString(TimeDayOfWeek(OrderOpenTime()));
   allTradesInHistory[placeToBeUsed][_closeDay] = f_convertStaticToDayString(TimeDayOfWeek(OrderCloseTime()));
   allTradesInHistory[placeToBeUsed][_openMonth] = f_convertStaticToMonthString(TimeMonth(OrderOpenTime()));
   allTradesInHistory[placeToBeUsed][_closeMonth] = f_convertStaticToMonthString(TimeMonth(OrderCloseTime()));
   allTradesInHistory[placeToBeUsed][_openYear] = (string)(TimeYear(OrderOpenTime()));
   allTradesInHistory[placeToBeUsed][_closeYear] = (string)(TimeYear(OrderCloseTime()));

   allTradesInHistory[placeToBeUsed][_orderCommission] = (string)OrderCommission();
   allTradesInHistory[placeToBeUsed][_orderSwap] = (string)OrderSwap();
   allTradesInHistory[placeToBeUsed][_orderProfit] = (string)OrderProfit();
   allTradesInHistory[placeToBeUsed][_stoploss] = (string)OrderStopLoss();
   allTradesInHistory[placeToBeUsed][_takeprofit] = (string)OrderTakeProfit();
   allTradesInHistory[placeToBeUsed][_magicNumber] = (string)OrderMagicNumber();
   allTradesInHistory[placeToBeUsed][_orderComment] = OrderComment();
   allTradesInHistory[placeToBeUsed][_slippage] = "0";
   if (StringFind(OrderComment(), "[sl]") >= 0 && TimeDayOfWeek(OrderCloseTime()) != SUNDAY)
      f_fillSlippageInCaseOfSL(placeToBeUsed);

   if (StringFind(OrderComment(), "[tp]") >= 0 && TimeDayOfWeek(OrderCloseTime()) != SUNDAY)
      f_fillSlippageInCaseOfTP(placeToBeUsed);

   allTradesInHistory[placeToBeUsed][_cmd] = f_getTradeTypeDescription(OrderType());

}

… relativ stupides Einfüllen der Daten auf die vorgesehenen Array-Plätze und die eine oder andere Berechnung…

Step 4: Optionales Ersetzen von Punkt durch Komma

Die meisten MetaTrader User werden sicherlich ein Betriebssystem verwenden, bei dem Komma als Standard-Separator eingestellt ist. MetaTrader würde ohne eine spezielle Aufforderung allerdings alle Gleitkommazahlen mit Punkt ausgeben statt mit Komma. Wenn Sie dann wiederum beispielsweise in Excel eine txt oder csv-Datei importieren, die Punkte als Trennzeichen hat, dann werden Sie ziemliches Chaos vorfinden.
Ich habe also eine externe Variable einprogrammiert, mit der man bei Bedarf alle Punkte durch Kommas ersetzen kann. Die Funktion dafür sieht wie folgt aus:

string f_checkForReplacementOfDotByComma(int placeToBeUsed, uint parameter)
{
   if(replace_Dot_By_Comma)
      StringReplace(allTradesInHistory[placeToBeUsed][parameter], ".", ",");

   return(allTradesInHistory[placeToBeUsed][parameter]);
}

… ich gebe also einen beliebigen Array-Platz des string-Arrays mit allen Daten in diese Funktion und je nachdem, ob replace_Dot_By_Comma true ist, oder nicht, werden alle Punkte durch Komma ersetzt, oder eben auch nicht. Falls nichts ersetzt werden muss, wird einfach der unveränderte Wert zurück gegeben.

So, ich denke das sollte reichen für eine grobe Beschreibung des ungefähren Ablaufs. Wer es genau wissen will kann wie immer den Quellcode anschauen, durcharbeiten, verändern, ausprobieren, nachfragen etc.


Zum Schluss der gesamte MQL-Quellcode und auch die sofort verwendbare ex4-Datei hier kostenlos zum Download für alle registrierten Nutzer und natürlich wie immer für jegliche private Nutzung frei verfügbar. Wer noch keinen User hat, einfach einen eigenen USER-Registrieren, valide Mail-Adresse eingeben und schon steht sämtlicher Quellcode aller Blog-Einträge zum Download zur Verfügung.
Wir übernehmen wie immer keine Garantie für die korrekte Funktionsweise dieses Tools bei allen MetaTrader Brokern die es auf der Welt gibt. Bitte auch dieses MetaTrader Tool wie immer bei Ihrem Broker testen und bei Fragen gerne melden.

Download MetaTrader Tool zum Loggen der Trade-Historie in txt oder csv

Viel Erfolg beim selber ausprobieren
Thomas @ ForexInnovation

*Seit vielen Jahren kennzeichnet MetaTrader SLs mit [sl] und TPs mit [tp] im OrderKommentar eines Trades insofern der Trade automatisch beim Erreichen dieses Wertes durch den Broker geschlossen wurde. Sollte sich das jemals ändern, oder verwendet jemand diese Kommentar selber, dann muss man diese Abfrage umbauen!



9 comments

Skip to comment form

  1. Mattteo

    Hallo Thomas,

    gibt es ein Tool oder Möglichkeit zum Einlesen/Importieren von Pending orders als .txt oder .cvs Dateien in den MT4?

    Ich plane und berechne meine Trades als Pending orders mit Entry, TP und SL in Excel und würde diese gern mit einem Skript (?) in den MT4 einlesen.

    Vorab danke für jeden Tipp…

    Gruß
    Maciej

    1. Thomas Rosenkranz

      Hi Maciej (sorry wenn ich Mattteo mit den drei schönen t’s übernommen hatte in meiner letzten Antwort …)

      das faszinierende an Programmierung: alles geht, ist am Ende nur eine Frage des Aufwandes.

      Schick doch mal eine Beispieldatei an info@code4trading.com, ich schau mal drauf.

      Grüße
      Thomas

  2. Joachim108

    Hallo Thomas,
    Metatrader behauptet, die Datei wäre ordnungsgemäss erzeugt – aber wo ist sie???

    1. Thomas Rosenkranz

      Hallo Joachim,

      MetaTrader und die Ordnerstruktur … bitte mal hier nachlesen bzw. einfach das Video anschauen: MetaTrader Ordnerstruktur

      Grüße
      Thomas

  3. fatlemming

    Hallo Thomas
    Danke für deine Mühe und deine Hilfe.

    In der Funktion: f_createNewFile()

    du schriebst: (string)AccountNumber(),”_Broker_”,AccountCompany(),”csv”); Fehlt da nicht der Punkt vor csv und txt?
    Also : (string)AccountNumber(),”_Broker_”,AccountCompany(),”.csv”); und “.txt”

    fatlemming

    1. Thomas Rosenkranz

      Hi fatlemming,

      ja, da hast Du Recht, den Punkt sollte man hinzufügen. MetaTrader erzeugt zwar trotzdem die Dateien, aber man hat keine Endung und muss die jedesmal noch durch das Einfügen des Punktes korrigieren.

      Danke fürs aufmerksame Lesen und den Hinweis!
      Thomas

  4. knuddel

    Hallo, ein wirklich gutes Tool.
    Hat zufällig jemand für den MS Excel ein Sheet zur Verfügung wo man die Daten Sortieren und nach Symbol auswerten kann?
    Grüße

  5. rood75

    Hallo Thomas,

    ich würde mir gern noch die pips anzeigen lassen generiert wurden und ob es ein long oder short Trade war.
    Bis jetzt habe ich es hinbekommen das ich mir den closePrice anzeigen lassen kann, aber ich habe keine Idee wie ich das berechnen soll, da es ja pro Währungspaar unterschiedlich ist.

    1. Thomas Rosenkranz

      kein Problem, here we go Tool-Erweiterung :-), viel Spaß damit.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*