Das MQL5-Kochbuch: Schreiben der Historie von Abschlüssen in eine Datei und Erstellen von Bilanzdiagrammen für jedes Symbol in Excel
Einleitung
Bei der Kommunikation in diversen Foren nutze ich oft Beispiele meiner Testergebnisse in der Darstellung in Form von Screenshots von Excel-Diagrammen. Ich werde häufig gebeten, zu erklären, wie solche Diagramme erstellt werden können. Excel bietet umfangreiche Funktionen zum Erstellen von Diagrammen und es gibt zahlreiche Bücher zu diesem Thema. Um die erforderliche Information in einem Buch zu finden, muss man es möglicherweise komplett lesen. Nun habe ich endlich etwas Zeit gefunden, alles in diesem Beitrag zu erklären.
In den beiden vorherigen Beiträgen Das MQL5-Kochbuch: Mehrwährungsfähiger Expert Advisor – eine einfache, saubere und schnelle Herangehensweise und Das MQL5-Kochbuch: Entwicklung eines mehrwährungsfähigen Expert Advisors mit unbegrenzter Anzahl von Parametern haben wir die Entwicklung von mehrwährungsfähigen EAs in MQL5 behandelt. Wir wissen, dass die Testergebnisse in MetaTrader 5 als allgemeine Bilanz-/Eigenkapitalkurve dargestellt werden, d. h., wenn Sie die Ergebnisse für jedes Symbol gesondert betrachten möchten, müssen Sie die externen Parameter des Expert Advisors immer wieder aufrufen, um alle Symbole außer demjenigen, dessen Ergebnisse Sie benötigen, zu deaktivieren, und dann den Test erneut ausführen. Das ist ziemlich unpraktisch.
Deshalb zeige ich Ihnen heute eine einfache Methode, mit der Sie Bilanzdiagramme für alle Symbole zusammen mit dem Gesamtergebnis eines mehrwährungsfähigen Expert Advisors in einem einzelnen Excel-Diagramm mit nur wenigen Klicks erhalten können. Um dieses Beispiel zu rekonstruieren, nehmen wir den mehrwährungsfähigen Expert Advisor aus dem vorherigen Beitrag. Er wird um eine Funktion erweitert, die die Historie von Abschlüssen und die Bilanzkurven für alle Symbole nach dem Abschluss des Tests in eine .csv-Datei schreibt. Außerdem fügen wir eine weitere Spalte im Bericht ein, in der die Wertverluste aus allen lokalen Maxima angezeigt werden.
Erstellen wir eine Excel-Tabelle mit der Möglichkeit, sich mit der Datendatei zu verbinden. Die Tabelle kann die ganze Zeit über geöffnet bleiben, also muss sie vor dem Ausführen eines weiteren Tests nicht geschlossen werden. Nach dem Abschluss des Tests müssen Sie lediglich die Daten aktualisieren, indem Sie einen bestimmten Knopf drücken, um die Änderungen im Bericht und im Diagramm sehen zu können.
Entwicklung des Expert Advisors
Wir werden keine großen Änderungen an unserem EA vornehmen, sondern lediglich ein paar Funktionen hinzufügen. Fangen wir an, indem wir die Struktur und das Array für Bilanzen von Symbolen zur Hauptdatei hinzufügen.
//--- Arrays for balances struct Balance { double balance[]; }; //--- Array of balances for all symbols Balance symbol_balance[];
Anschließend erstellen wir separat die Include-Datei Report.mqh für Funktionen, die Testberichte erzeugen, und binden sie in der Hauptdatei des Expert Advisors ein (siehe markierte Zeile im nachfolgenden Code):
//--- Include custom libraries #include "Include/Auxiliary.mqh" #include "Include/Enums.mqh" #include "Include/Errors.mqh" #include "Include/FileFunctions.mqh" #include "Include/InitializeArrays.mqh" #include "Include/Report.mqh" #include "Include/ToString.mqh" #include "Include/TradeFunctions.mqh" #include "Include/TradeSignals.mqh"
Erstellen wir als Erstes eine Struktur für die Eigenschaften von Abschlüssen ähnlich der, die wir in unserem Projekt bereits für die Eigenschaften von Positionen und Symbolen haben. Dazu fügen wir die Aufzählung der Identifikatoren von Eigenschaften zur Datei Enums.mqh hinzu:
//+------------------------------------------------------------------+ //| Enumeration of deal properties | //+------------------------------------------------------------------+ enum ENUM_DEAL_PROPERTIES { D_SYMBOL = 0, // Deal symbol D_COMMENT = 1, // Deal comment D_TYPE = 2, // Deal type D_ENTRY = 3, // Deal entry - entry in, entry out, reverse D_PRICE = 4, // Deal price D_PROFIT = 5, // Deal result (profit/loss) D_VOLUME = 6, // Deal volume D_SWAP = 7, // Cumulative swap on close D_COMMISSION = 8, // Deal commission D_TIME = 9, // Deal time D_ALL = 10 // All of the above mentioned deal properties };
Anschließend erstellen wir in der Datei Report.mqh die Struktur der Eigenschaften von Abschlüssen und die Funktion GetHistoryDealProperties(), die die Eigenschaft eines Abschlusses ausgibt. Die Funktion akzeptiert zwei Parameter: Ticket des Abschlusses und Identifikator der Eigenschaft.
Nachfolgend sehen Sie den Code der Struktur und der Funktion GetHistoryDealProperties():
//--- Deal properties in the history struct HistoryDealProperties { string symbol; // Symbol string comment; // Comment ENUM_DEAL_TYPE type; // Deal type ENUM_DEAL_ENTRY entry; // Direction double price; // Price double profit; // Profit/Loss double volume; // Volume double swap; // Swap double commission; // Commission datetime time; // Time }; //--- Variable of deal properties HistoryDealProperties deal; //+------------------------------------------------------------------+ //| Gets deal properties by ticket | //+------------------------------------------------------------------+ void GetHistoryDealProperties(ulong ticket_number,ENUM_DEAL_PROPERTIES history_deal_property) { switch(history_deal_property) { case D_SYMBOL : deal.symbol=HistoryDealGetString(ticket_number,DEAL_SYMBOL); break; case D_COMMENT : deal.comment=HistoryDealGetString(ticket_number,DEAL_COMMENT); break; case D_TYPE : deal.type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket_number,DEAL_TYPE); break; case D_ENTRY : deal.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_number,DEAL_ENTRY); break; case D_PRICE : deal.price=HistoryDealGetDouble(ticket_number,DEAL_PRICE); break; case D_PROFIT : deal.profit=HistoryDealGetDouble(ticket_number,DEAL_PROFIT); break; case D_VOLUME : deal.volume=HistoryDealGetDouble(ticket_number,DEAL_VOLUME); break; case D_SWAP : deal.swap=HistoryDealGetDouble(ticket_number,DEAL_SWAP); break; case D_COMMISSION : deal.commission=HistoryDealGetDouble(ticket_number,DEAL_COMMISSION); break; case D_TIME : deal.time=(datetime)HistoryDealGetInteger(ticket_number,DEAL_TIME); break; case D_ALL : deal.symbol=HistoryDealGetString(ticket_number,DEAL_SYMBOL); deal.comment=HistoryDealGetString(ticket_number,DEAL_COMMENT); deal.type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket_number,DEAL_TYPE); deal.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket_number,DEAL_ENTRY); deal.price=HistoryDealGetDouble(ticket_number,DEAL_PRICE); deal.profit=HistoryDealGetDouble(ticket_number,DEAL_PROFIT); deal.volume=HistoryDealGetDouble(ticket_number,DEAL_VOLUME); deal.swap=HistoryDealGetDouble(ticket_number,DEAL_SWAP); deal.commission=HistoryDealGetDouble(ticket_number,DEAL_COMMISSION); deal.time=(datetime)HistoryDealGetInteger(ticket_number,DEAL_TIME); break; //--- default: Print("The passed deal property is not listed in the enumeration!"); return; } }
Wir werden außerdem mehrere Funktionen brauchen, die einige Eigenschaften von Abschlüssen in String-Werte umwandeln. Diese einfachen Funktionen geben einen Bindestrich ("-") aus, wenn der übergebene wert leer oder Null ist. Schreiben wir sie in die Datei ToString.mqh:
//+------------------------------------------------------------------+ //| Returns the symbol name, otherwise - dash | //+------------------------------------------------------------------+ string DealSymbolToString(string deal_symbol) { return(deal_symbol=="" ? "-" : deal_symbol); } //+------------------------------------------------------------------+ //| Converts deal type to string | //+------------------------------------------------------------------+ string DealTypeToString(ENUM_DEAL_TYPE deal_type) { string str=""; //--- switch(deal_type) { case DEAL_TYPE_BUY : str="buy"; break; case DEAL_TYPE_SELL : str="sell"; break; case DEAL_TYPE_BALANCE : str="balance"; break; case DEAL_TYPE_CREDIT : str="credit"; break; case DEAL_TYPE_CHARGE : str="charge"; break; case DEAL_TYPE_CORRECTION : str="correction"; break; case DEAL_TYPE_BONUS : str="bonus"; break; case DEAL_TYPE_COMMISSION : str="commission"; break; case DEAL_TYPE_COMMISSION_DAILY : str="commission daily"; break; case DEAL_TYPE_COMMISSION_MONTHLY : str="commission monthly"; break; case DEAL_TYPE_COMMISSION_AGENT_DAILY : str="commission agent daily"; break; case DEAL_TYPE_COMMISSION_AGENT_MONTHLY : str="commission agent monthly"; break; case DEAL_TYPE_INTEREST : str="interest"; break; case DEAL_TYPE_BUY_CANCELED : str="buy canceled"; break; case DEAL_TYPE_SELL_CANCELED : str="sell canceled"; break; //--- Unknown deal type default : str="unknown"; } //--- return(str); } //+------------------------------------------------------------------+ //| Converts direction of deal to string | //+------------------------------------------------------------------+ string DealEntryToString(ENUM_DEAL_ENTRY deal_entry) { string str=""; //--- switch(deal_entry) { case DEAL_ENTRY_IN : str="in"; break; case DEAL_ENTRY_OUT : str="out"; break; case DEAL_ENTRY_INOUT : str="in/out"; break; case DEAL_ENTRY_STATE : str="status record"; break; //--- Unknown direction type default : str="unknown"; } //--- return(str); } //+------------------------------------------------------------------+ //| Converts volume to string | //+------------------------------------------------------------------+ string DealVolumeToString(double deal_volume) { return(deal_volume<=0 ? "-" : DoubleToString(deal_volume,2)); } //+------------------------------------------------------------------+ //| Converts price to string | //+------------------------------------------------------------------+ string DealPriceToString(double deal_price,int digits) { return(deal_price<=0 ? "-" : DoubleToString(deal_price,digits)); } //+------------------------------------------------------------------+ //| Converts deal result to string | //+------------------------------------------------------------------+ string DealProfitToString(string deal_symbol,double deal_profit) { return((deal_profit==0 || deal_symbol=="") ? "-" : DoubleToString(deal_profit,2)); } //+------------------------------------------------------------------+ //| Converts swap to string | //+------------------------------------------------------------------+ string DealSwapToString(double deal_swap) { return(deal_swap<=0 ? "-" : DoubleToString(deal_swap,2)); }
Nun haben wir alles, was wir zum Schreiben der Funktion CreateSymbolBalanceReport() benötigen, die die Daten für den Bericht vorbereitet und sie in die Datei LastTest.csv schreibt. Es ist ziemlich simpel: Als Erstes schreiben wir die Kopfzeile (achten Sie darauf, wie der String angepasst wird, falls der Test für mehr als ein Symbol ausgeführt wurde), dann werden die erforderlichen Eigenschaften von Abschlüssen für den Bericht zu einem String verbunden, der anschließend in die Datei geschrieben wird.
Nachfolgend sehen Sie den Code der Funktion CreateSymbolBalanceReport():
//+------------------------------------------------------------------+ //| Creates the test report on deals in .csv format | //+------------------------------------------------------------------+ void CreateSymbolBalanceReport() { int file_handle =INVALID_HANDLE; // File handle string path =""; // File path //--- If an error occurred when creating/getting the folder, exit if((path=CreateInputParametersFolder())=="") return; //--- Create file to write data in the common folder of the terminal file_handle=FileOpen(path+"\\LastTest.csv",FILE_CSV|FILE_WRITE|FILE_ANSI|FILE_COMMON); //--- If the handle is valid (file created/opened) if(file_handle>0) { int digits =0; // Number of decimal places in the price int deals_total =0; // Number of deals in the specified history ulong ticket =0; // Deal ticket double drawdown_max =0.0; // Maximum drawdown double balance =0.0; // Balance //--- string delimeter =","; // Delimiter string string_to_write =""; // To generate the string for writing //--- Generate the header string string headers="TIME,SYMBOL,DEAL TYPE,ENTRY TYPE,VOLUME,PRICE,SWAP($),PROFIT($),DRAWDOWN(%),BALANCE"; //--- If more than one symbol is involved, modify the header string if(SYMBOLS_COUNT>1) { for(int s=0; s<SYMBOLS_COUNT; s++) StringAdd(headers,","+InputSymbols[s]); } //--- Write the report headers FileWrite(file_handle,headers); //--- Get the complete history HistorySelect(0,TimeCurrent()); //--- Get the number of deals deals_total=HistoryDealsTotal(); //--- Resize the array of balances according to the number of symbols ArrayResize(symbol_balance,SYMBOLS_COUNT); //--- Resize the array of deals for each symbol for(int s=0; s<SYMBOLS_COUNT; s++) ArrayResize(symbol_balance[s].balance,deals_total); //--- Iterate in a loop and write the data for(int i=0; i<deals_total; i++) { //--- Get the deal ticket ticket=HistoryDealGetTicket(i); //--- Get all the deal properties GetHistoryDealProperties(ticket,D_ALL); //--- Get the number of digits in the price digits=(int)SymbolInfoInteger(deal.symbol,SYMBOL_DIGITS); //--- Calculate the overall balance balance+=deal.profit+deal.swap+deal.commission; //--- Generate a string for writing via concatenation StringConcatenate(string_to_write, deal.time,delimeter, DealSymbolToString(deal.symbol),delimeter, DealTypeToString(deal.type),delimeter, DealEntryToString(deal.entry),delimeter, DealVolumeToString(deal.volume),delimeter, DealPriceToString(deal.price,digits),delimeter, DealSwapToString(deal.swap),delimeter, DealProfitToString(deal.symbol,deal.profit),delimeter, MaxDrawdownToString(i,balance,max_drawdown),delimeter, DoubleToString(balance,2)); //--- If more than one symbol is involved, write their balance values if(SYMBOLS_COUNT>1) { //--- Iterate over all symbols for(int s=0; s<SYMBOLS_COUNT; s++) { //--- If the symbols are equal and the deal result is non-zero if(deal.symbol==InputSymbols[s] && deal.profit!=0) { //--- Display the deal in the balance for the corresponding symbol // Take into consideration swap and commission symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]+ deal.profit+ deal.swap+ deal.commission; //--- Add to the string StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } //--- Otherwise write the previous value else { //--- If the deal type is "Balance" (the first deal) if(deal.type==DEAL_TYPE_BALANCE) { //--- the balance is the same for all symbols symbol_balance[s].balance[i]=balance; StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } //--- Otherwise write the previous value to the current index else { symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]; StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } } } } //--- Write the generated string FileWrite(file_handle,string_to_write); //--- Mandatory zeroing out of the variable for the next string string_to_write=""; } //--- Close the file FileClose(file_handle); } //--- If the file could not be created/opened, print the appropriate message else Print("Error creating file: "+IntegerToString(GetLastError())+""); }
Die im Code markierte Funktion MaxDrawdownToString() berechnet alle Wertverluste aus lokalen Maxima und gibt eine String-Darstellung der Zeit aus, zu der ein neues lokales Maximum auftritt. In allen anderen Fällen gibt die Funktion einen String mit einem "-" (Bindestrich) aus.
//+------------------------------------------------------------------+ //| Returns the maximum drawdown from the local maximum | //+------------------------------------------------------------------+ string MaxDrawdownToString(int deal_number,double balance,double &max_drawdown) { //--- The string to be displayed in the report string str=""; //--- To calculate the local maximum and drawdown static double max=0.0; static double min=0.0; //--- If this is the first deal if(deal_number==0) { //--- No drawdown yet max_drawdown=0.0; //--- Set the initial point as the local maximum max=balance; min=balance; } else { //--- If the current balance is greater than in the memory if(balance>max) { //--- calculate the drawdown using the previous values max_drawdown=100-((min/max)*100); //--- update the local maximum max=balance; min=balance; } else { //--- Return zero value of the drawdown max_drawdown=0.0; //--- Update the minimum min=fmin(min,balance); } } //--- Determine the string for the report if(max_drawdown==0) str="-"; else str=DoubleToString(max_drawdown,2); //--- Return result return(str); }
Alle Funktionen für die Erzeugung des Berichts sind also fertig. Wir müssen nur noch herausfinden, wie wir all das nutzen können. Dazu wird die Funktion OnTester() benötigt, die beim Abschluss des Testings aufgerufen wird. Stellen Sie sicher, dass Sie die detaillierte Beschreibung dieser Funktion im Nachschlagewerk MQL5 ansehen.
Schreiben Sie einfach einige Codezeilen in den Körper der Funktion OnTester(), in denen Sie die Bedingung festlegen, unter der der Bericht erzeugt werden soll. Den entsprechenden Code-Ausschnitt sehen Sie unten:
//+------------------------------------------------------------------+ //| Handler of the event of testing completion | //+------------------------------------------------------------------+ double OnTester() { //--- Write the report only after testing if(IsTester() && !IsOptimization() && !IsVisualMode()) //--- Generate the report and write it to the file CreateSymbolBalanceReport(); //--- return(0.0); }
Wenn Sie nun den Expert Advisor im Strategietester ausführen, sehen Sie am Ende des Tests einen Ordner des Expert Advisors im Terminal-Stammverzeichnis C:\ProgramData\MetaQuotes\Terminal\Common\Files. Außerdem wird die Bericht-Datei LastTest.csv im Ordner des Expert Advisors erzeugt. Wenn Sie die Datei im Editor öffnen, sehen Sie in etwa Folgendes:
Abbildung 1. Bericht-Datei im .csv-Format.
Erstellen von Diagrammen in Excel
Wir können die erstellte Datei in Excel öffnen und sehen, dass sich jeder Datentyp in einer separaten Spalte befindet. Auf diese Art können die Daten viel bequemer betrachtet werden. An dieser Stelle sind wir technisch gesehen bereit, Diagramme zu erstellen und die Datei als Excel-Tabelle im Format *.xlsx zu speichern. Wenn wir allerdings anschließend den Test ausführen und die Tabelle erneut öffnen, sehen wir immer noch die alten Daten.
Wenn wir versuchen, die Daten zu aktualisieren, während die Datei LastTest.csv bereits in Excel verwendet wird, wird die Datei nicht aktualisiert, da es dem Expert Advisor nicht möglich ist, die Datei zum Schreiben zu öffnen, während sie von einer anderen Anwendung verwendet wird.
Abbildung 2. Bericht-Datei im .csv-Format in Excel 2010.
Dafür gibt es in unserem Fall eine Lösung. Erstellen Sie zunächst eine Excel-Tabelle im Format *.xlsx in einem beliebigen Ordner. Öffnen Sie die Tabelle und gehen Sie zur Registerkarte Daten.
Abbildung 3. Registerkarte Daten in Excel 2010.
Wählen Sie im Ribbon dieser Registerkarte die Option Aus Text. Es öffnet sich der Dialog Textdatei importieren, in dem Sie die Datei "LastTest.csv" auswählen. Wählen Sie die Datei aus und klicken Sie auf Öffnen. Der Dialog Textkonvertierungs-Assistent - Schritt 1 von 3 öffnet sich, wie unten dargestellt:
Abbildung 4. Dialog "Textkonvertierungs-Assistent - Schritt 1 von 3".
Passen Sie die Einstellungen an, wie oben dargestellt, und klicken Sie auf Weiter >. Hier (Schritt 2 von 3) müssen Sie das in der Datendatei genutzte Trennzeichen festlegen. In unserem Fall ist es "," (Komma).
Abbildung 5. Dialog "Textkonvertierungs-Assistent - Schritt 2 von 3".
Klicken Sie auf Weiter >, um mit Textkonvertierungs-Assistent - Schritt 3 von 3 fortzufahren. Wählen Sie hier Standard als Datenformat für alle Spalten. Sie können das Format später ändern.
Abbildung 6. Dialog "Textkonvertierungs-Assistent - Schritt 3 von 3".
Nachdem Sie auf Fertig stellen geklickt haben, öffnet sich das Fenster Daten importieren, in dem Sie das Arbeitsblatt und die Zelle für den Datenimport festlegen müssen.
Abbildung 7. Auswahl der Zelle für den Datenimport in Excel 2010.
Für gewöhnlich wählen wir die obere linke Zelle A1. Bevor Sie auf OK klicken, klicken Sie auf Eigenschaften..., um die Eigenschaften des externen Datenbereiches festzulegen. Sie sehen das unten abgebildete Dialogfenster.
Abbildung 8. Eigenschaften des externen Datenbereichs beim Importieren von Daten aus Textdateien in Excel 2010.
Passen Sie die Einstellungen an, wie oben abgebildet, und klicken Sie in diesem und dem nachfolgenden Fenster auf OK.
Als Ergebnis erscheinen Ihre Daten in der gleichen Form, als hätten Sie einfach die .csv-Datei geladen. Doch nun können Sie wiederholte Tests in MetaTrader 5 ausführen, ohne die Excel-Tabelle schließen zu müssen. Nach dem Test müssen sie lediglich mithilfe von Strg+Alt+F5 die Daten aktualisieren oder auf die Schaltfläche Alle aktualisieren im Ribbon der Registerkarte Daten klicken.
Mithilfe der Bedingten Formatierung in der Registerkarte Start können Sie die erforderlichen optischen Eigenschaften der Darstellung der Daten festlegen.
Abbildung 9. Bedingte Formatierung in Excel 2010.
Nun müssen wir die Daten in Excel-Diagrammen abbilden. Ein Diagramm wird alle Bilanzdiagramme anzeigen, das andere alle Wertverluste aus lokalen Maxima als Histogramm.
Erstellen wir als Erstes ein Diagramm für die Bilanzdiagramme. Wählen Sie die Überschriften aller Bilanzen und den gesamten Datenbereich von oben nach unten (halten Sie dabei die Umschalttaste gedrückt, drücken Sie die Taste Ende und dann den Pfeil nach unten). Wählen Sie nun unter der Registerkarte Einfügen den erforderlichen Diagrammtyp.
Abbildung 10. Auswahl des Diagrammtypen in Excel 2010.
Es wird ein Diagramm erstellt, das zur Erleichterung der Arbeit in ein anderes Arbeitsblatt verschoben werden kann. Wählen Sie es dazu einfach aus und drücken Sie Strg+X (Ausschneiden). Wechseln Sie dann zum neu erstellten Arbeitsblatt, wählen Sie die Zelle A1 und drücken Sie Strg+V (Einfügen).
Das erstellte Diagramm mit Standardeinstellungen ist in der nachfolgenden Abbildung dargestellt:
Abbildung 11. Look and Feel des Diagramms mit Standardeinstellungen.
Sie können jedes Element des Diagramms personalisieren: Größe, Farbe Stil usw. ändern.
In der obigen Abbildung zeigt die horizontale Achse die Anzahl der Abschlüsse. Verändern wir sie dahingehend, dass sie stattdessen Daten anzeigt. Rechtsklicken Sie hierzu auf das Diagramm und wählen Sie die Option Daten auswählen... aus dem Kontextmenü. Es erscheint der Dialog Datenquelle auswählen. Klicken Sie auf Bearbeiten, dann auf den erforderlichen Datenbereich in der Spalte TIME und zum Schluss auf OK.
Abbildung 12. Dialogfenster "Datenquelle auswählen".
Versuchen Sie, das Diagramm der Wertverluste selbst zu erstellen, und platzieren Sie es unter dem ersten Diagramm. Nun können ihre optischen Eigenschaften angepasst werden, falls erforderlich. Ich persönlich gestalte sie für gewöhnlich so:
Abbildung 13. Benutzerdefinierte Diagramme in Excel 2010.
Fazit
Nun verfügen wir über Excel-Diagramme mit Testergebnissen, die ziemlich gut aussehen. In einem meiner zukünftigen Beiträge werde ich Ihnen zeigen, wie Sie noch informationsreichere Berichte erstellen können. Im Anhang des Beitrags finden Sie ein Archiv mit den Dateien des Expert Advisors.
Legen Sie den Ordner ReportInExcel nach dem Entpacken der Dateien in das Verzeichnis MetaTrader 5\MQL5\Experts. Außerdem muss der Indikator EventsSpy.mq5 im Ordner MetaTrader 5\MQL5\Indicators abgelegt werden.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/651
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.