Ich habe einen etwas anderen Ansatz. Da jeder Roboter ein Protokoll über seine Trades schreibt, reicht es, ein Skript zu erstellen. Wenn ich es auf einem Symbolchart ausführe, fragt es nach der Roboternummer (magic) und lädt aus der Datei mit diesem Symbol und dieser Nummer im Namen die Trades - alle oder nur long/nur short - auf den Chart (es kann sinnvoll sein, sie zu trennen). Dieser Ansatz erlaubt es, mit Netting zu arbeiten, was für mich grundlegend wichtig ist. Und auch mit Robotern, die im Testmodus handeln - ohne Trades zu machen, sondern nur simulieren, berechnen und die Ergebnisse in einer Datei aufzeichnen.
//+------------------------------------------------------------------+ //|| Transaktionsstruktur. Wird verwendet, um eine Datei mit der Historie der Transaktionen zu erstellen. //+------------------------------------------------------------------+ struct SDeal { ulong ticket; // Deal Ticket long order; // Bestellung, auf deren Grundlage das Geschäft eröffnet wurde long pos_id; // Positionsbezeichner long time_msc; // Zeit in Millisekunden datetime time; // Zeit. double volume; // Lautstärke double price; // Preis double profit; // Gewinn double commission; // Provision für die Transaktion double swap; // Kumulierter Swap bei Abschluss double fee; // Zahlung für die Transaktion, die sofort nach der Transaktion berechnet wird double sl; // Stop-Loss-Niveau double tp; // Gewinnmitnahme-Niveau ENUM_DEAL_TYPE type; // Typ ENUM_DEAL_ENTRY entry; // Methode zur Änderung der Position ENUM_DEAL_REASON reason; // Grund oder Quelle für die Durchführung der Transaktion long magic; // Experten-ID int digits; // Ziffernzeichen ushort symbol[16]; // Symbol ushort comment[64]; // Kommentar zur Transaktion ushort external_id[256]; // Geschäftsidentifikator im externen Handelssystem (an der Börse) //--- Einstellung von Zeichenketteneigenschaften bool SetSymbol(const string deal_symbol) { return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length()); } bool SetComment(const string deal_comment) { return(::StringToShortArray(deal_comment, comment)==deal_comment.Length()); } bool SetExternalID(const string deal_external_id) { return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); } //--- Rückgabe von String-Eigenschaften string Symbol(void) { return(::ShortArrayToString(symbol)); } string Comment(void) { return(::ShortArrayToString(comment)); } string ExternalID(void) { return(::ShortArrayToString(external_id)); } }; //+------------------------------------------------------------------+ //| Speichert Transaktionen aus der Historie in ein Array //+------------------------------------------------------------------+ int SaveDealsToArray(SDeal &array[], bool logs=false) { //--- Transaktionsstruktur SDeal deal={}; //--- Abfrage des Verlaufs der Geschäfte im Zeitraum vom Beginn bis zum aktuellen Zeitpunkt if(!HistorySelect(0, TimeCurrent())) { Print("HistorySelect() failed. Error ", GetLastError()); return 0; } //--- Gesamtzahl der Geschäfte in der Liste int total=HistoryDealsTotal(); //--- jede Transaktion verarbeiten for(int i=0; i<total; i++) { //--- ein Ticket für das nächste Geschäft erhalten (das Geschäft wird automatisch ausgewählt, um seine Eigenschaften zu erhalten) ulong ticket=HistoryDealGetTicket(i); if(ticket==0) continue; //--- nur Bilanz und Handelsgeschäfte speichern ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE); if(deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL && deal_type!=DEAL_TYPE_BALANCE) continue; //--- Speichern von Transaktionseigenschaften in der Struktur deal.ticket=ticket; deal.type=deal_type; deal.order=HistoryDealGetInteger(ticket, DEAL_ORDER); deal.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket, DEAL_ENTRY); deal.reason=(ENUM_DEAL_REASON)HistoryDealGetInteger(ticket, DEAL_REASON); deal.time=(datetime)HistoryDealGetInteger(ticket, DEAL_TIME); deal.time_msc=HistoryDealGetInteger(ticket, DEAL_TIME_MSC); deal.pos_id=HistoryDealGetInteger(ticket, DEAL_POSITION_ID); deal.volume=HistoryDealGetDouble(ticket, DEAL_VOLUME); deal.price=HistoryDealGetDouble(ticket, DEAL_PRICE); deal.profit=HistoryDealGetDouble(ticket, DEAL_PROFIT); deal.commission=HistoryDealGetDouble(ticket, DEAL_COMMISSION); deal.swap=HistoryDealGetDouble(ticket, DEAL_SWAP); deal.fee=HistoryDealGetDouble(ticket, DEAL_FEE); deal.sl=HistoryDealGetDouble(ticket, DEAL_SL); deal.tp=HistoryDealGetDouble(ticket, DEAL_TP); deal.magic=HistoryDealGetInteger(ticket, DEAL_MAGIC); deal.SetSymbol(HistoryDealGetString(ticket, DEAL_SYMBOL)); deal.SetComment(HistoryDealGetString(ticket, DEAL_COMMENT)); deal.SetExternalID(HistoryDealGetString(ticket, DEAL_EXTERNAL_ID)); deal.digits=(int)SymbolInfoInteger(deal.Symbol(), SYMBOL_DIGITS); //--- das Array inkrementieren und int size=(int)array.Size(); ResetLastError(); if(ArrayResize(array, size+1, total)!=size+1) { Print("ArrayResize() failed. Error ", GetLastError()); continue; } //--- die Transaktion im Array speichern array[size]=deal; //--- falls aktiviert, die Beschreibung der gespeicherten Transaktion in das Journal ausgeben if(logs) DealPrint(deal, i); } //--- Rückgabe der Anzahl der im Array gespeicherten Geschäfte return (int)array.Size(); }
Ich verstehe nicht, warum die Logik der Architektur ignoriert wird.
- Wenn das Array Element für Element gefüllt wird, sollte es eine Funktion zum Füllen eines Elements geben.
- Wenn ein Element eine Struktur ist, sollte es von selbst gefüllt werden.
- Wenn Sie einen Reservespeicherplatz für das Array auf einmal zuweisen, warum müssen Sie dann bei jedem Füllen eine "Größenänderung" durchführen?
Warum schreibt man nicht zum Beispiel so?
//+------------------------------------------------------------------+ //|| Transaktionsstruktur. Wird verwendet, um eine Datei mit der Historie der Transaktionen zu erstellen. //+------------------------------------------------------------------+ struct SDeal { ulong ticket; // Deal Ticket long order; // Bestellung, auf deren Grundlage das Geschäft eröffnet wurde long pos_id; // Positionsbezeichner long time_msc; // Zeit in Millisekunden datetime time; // Zeit. double volume; // Lautstärke double price; // Preis double profit; // Gewinn double commission; // Provision für die Transaktion double swap; // Kumulierter Swap bei Abschluss double fee; // Zahlung für die Transaktion, die sofort nach der Transaktion berechnet wird double sl; // Stop-Loss-Niveau double tp; // Gewinnmitnahme-Niveau ENUM_DEAL_TYPE type; // Typ ENUM_DEAL_ENTRY entry; // Methode zur Änderung der Position ENUM_DEAL_REASON reason; // Grund oder Quelle für die Durchführung der Transaktion long magic; // Experten-ID int digits; // Ziffernzeichen ushort symbol[16]; // Symbol ushort comment[64]; // Kommentar zur Transaktion ushort external_id[256]; // Geschäftsidentifikator im externen Handelssystem (an der Börse) //--- Einstellung von Zeichenketteneigenschaften bool SetSymbol(const string deal_symbol) { return(::StringToShortArray(deal_symbol, symbol)==deal_symbol.Length()); } bool SetComment(const string deal_comment) { return(::StringToShortArray(deal_comment, comment)==deal_comment.Length()); } bool SetExternalID(const string deal_external_id) { return(::StringToShortArray(deal_external_id, external_id)==deal_external_id.Length()); } //--- Rückgabe von String-Eigenschaften string Symbol(void) { return(::ShortArrayToString(symbol)); } string Comment(void) { return(::ShortArrayToString(comment)); } string ExternalID(void) { return(::ShortArrayToString(external_id)); } bool Set( const ulong _ticket ) { this.ticket=_ticket; this.type=(ENUM_DEAL_TYPE)HistoryDealGetInteger(_ticket, DEAL_TYPE); this.order=HistoryDealGetInteger(_ticket, DEAL_ORDER); this.entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(_ticket, DEAL_ENTRY); this.reason=(ENUM_DEAL_REASON)HistoryDealGetInteger(_ticket, DEAL_REASON); this.time=(datetime)HistoryDealGetInteger(_ticket, DEAL_TIME); this.time_msc=HistoryDealGetInteger(_ticket, DEAL_TIME_MSC); this.pos_id=HistoryDealGetInteger(_ticket, DEAL_POSITION_ID); this.volume=HistoryDealGetDouble(_ticket, DEAL_VOLUME); this.price=HistoryDealGetDouble(_ticket, DEAL_PRICE); this.profit=HistoryDealGetDouble(_ticket, DEAL_PROFIT); this.commission=HistoryDealGetDouble(_ticket, DEAL_COMMISSION); this.swap=HistoryDealGetDouble(_ticket, DEAL_SWAP); this.fee=HistoryDealGetDouble(_ticket, DEAL_FEE); this.sl=HistoryDealGetDouble(_ticket, DEAL_SL); this.tp=HistoryDealGetDouble(_ticket, DEAL_TP); this.magic=HistoryDealGetInteger(_ticket, DEAL_MAGIC); this.SetSymbol(HistoryDealGetString(_ticket, DEAL_SYMBOL)); this.SetComment(HistoryDealGetString(_ticket, DEAL_COMMENT)); this.SetExternalID(HistoryDealGetString(_ticket, DEAL_EXTERNAL_ID)); this.digits=(int)SymbolInfoInteger(this.Symbol(), SYMBOL_DIGITS); return((bool)this.time); } }; bool SetDeal( SDeal &deal, const ulong ticket ) { //--- nur Bilanz und Handelsgeschäfte speichern const ENUM_DEAL_TYPE deal_type = (ENUM_DEAL_TYPE)HistoryDealGetInteger(ticket, DEAL_TYPE); return((deal_type!=DEAL_TYPE_BUY && deal_type!=DEAL_TYPE_SELL && deal_type!=DEAL_TYPE_BALANCE) && deal.Set(ticket)); } //+------------------------------------------------------------------+ //| Speichert Transaktionen aus der Historie in ein Array //+------------------------------------------------------------------+ int SaveDealsToArray(SDeal &array[], bool logs=false) { int Amount = 0; //--- Abfrage des Verlaufs der Geschäfte im Zeitraum vom Beginn bis zum aktuellen Zeitpunkt if(HistorySelect(0, INT_MAX)) { //--- Gesamtzahl der Geschäfte in der Liste const int total=ArrayResize(array, HistoryDealsTotal()); //--- jede Transaktion verarbeiten for(int i=0; i<total; i++) if (SetDeal(array[Amount], HistoryDealGetTicket(i))) Amount++; } //--- Rückgabe der Anzahl der im Array gespeicherten Geschäfte return(ArrayResize(array, Amount)); }
Ich selbst bin weit davon entfernt, Code auf vorbildliche Weise zu schreiben. Aber lassen Sie uns irgendwie vernünftiger sein.
Da haben wir es wieder.
//+------------------------------------------------------------------+ //| Gibt eine Beschreibung der Transaktion zurück| //+------------------------------------------------------------------+ string DealDescription(SDeal &deal, const int index) { string indexs=StringFormat("% 5d", index); if(deal.type!=DEAL_TYPE_BALANCE) return(StringFormat("%s: deal #%I64u %s, type %s, Position #%I64d %s (magic %I64d), Price %.*f at %s, sl %.*f, tp %.*f", indexs, deal.ticket, DealEntryDescription(deal.entry), DealTypeDescription(deal.type), deal.pos_id, deal.Symbol(), deal.magic, deal.digits, deal.price, TimeToString(deal.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS), deal.digits, deal.sl, deal.digits, deal.tp)); else return(StringFormat("%s: deal #%I64u %s, type %s %.2f %s at %s", indexs, deal.ticket, DealEntryDescription(deal.entry), DealTypeDescription(deal.type), deal.profit, AccountInfoString(ACCOUNT_CURRENCY), TimeToString(deal.time))); }
Warum druckt sich die Struktur nicht selbst aus?
Wenn ein Element eine Struktur ist, sollte es sich selbst ausfüllen.
Zeigen Sie bitte die Standardstrukturen für die Selbstveröffentlichung und das Selbstausfüllen.
Diese Codes wurden geschrieben, um das Verständnis zu erleichtern.
Es ist nicht das Ziel, alles in ein unlesbares, einzeiliges Monstrum zu verpacken, selbst wenn es funktioniert.
Artikel werden geschrieben, um das Wesentliche so klar wie möglich zu vermitteln, nicht um die Virtuosität des Codes zu zeigen. Es gibt spezielle Threads für Virtuosität, und Sie beteiligen sich an ihnen. Und sie sind nützlich.
Artikel haben einen anderen Zweck.
Zeigen Sie bitte die standardmäßigen selbstdruckenden und selbstausfüllenden Strukturen.
Wenn es sich um MQ-Strukturen handelt, sind sie in MQL5 nicht verfügbar. Die Autoren konnten nicht wissen, was und wer sie brauchen würde. Daher gibt es nur die Basis und die Möglichkeit der Vererbung von ihnen, um Standardstrukturen mit der vom Benutzer benötigten Funktionalität auszustatten.
Diese Codes wurden geschrieben, um das Verständnis zu erleichtern.
Es gibt keinen Grund, alles in ein unlesbares, in eine Zeile gepacktes Monstrum zu verpacken, selbst wenn es funktioniert.
Ich schlage so etwas nicht vor.
Artikel werden geschrieben, um das Wesentliche so klar wie möglich zu vermitteln, nicht die Virtuosität des Codeschreibens. Für Virtuosität gibt es spezielle Threads, an denen Sie teilnehmen. Und sie sind nützlich.
Artikel haben einen anderen Zweck.
Virtuosität ist der Autor des Lehrbuchs. Ich bin für die Logik.
Wenn ein Objekt etwas mit sich selbst und ohne Hilfe machen kann, sollte es das tun, nicht für sich selbst. Es sollte eine minimale Hierarchie in der Architektur der Codeerstellung geben. In Ihrem Code schlagen Sie lediglich vor, mehrere separate Arrays von Transaktionseigenschaften durch ein einziges Array von Transaktionseigenschaften unter Verwendung einer Struktur zu ersetzen. Nun, Sie verwenden fast nichts von der Struktur (Lesen/Schreiben und Gleichsetzen). Der gleiche prozedurale Stil der Arbeit mit Transaktionseigenschaften.
Wenn es sich um MQ-Strukturen handelt, sind sie in MQL5 nicht verfügbar. Die Autoren konnten nicht wissen, was und wer sie brauchen würde. Daher gibt es nur die Basis und die Möglichkeit der Vererbung von ihnen, um Standardstrukturen mit der vom Benutzer benötigten Funktionalität auszustatten.
Ich biete so etwas nicht an.
Die Virtuosität ist der Autor des Tutorials. Ich vertrete die Logik.
Wenn ein Objekt etwas mit sich selbst und ohne Hilfe tun kann, sollte es es tun, nicht für es. Es sollte eine minimale Hierarchie in der Architektur des geschriebenen Codes geben. In Ihrem Code schlagen Sie lediglich vor, mehrere separate Arrays von Transaktionseigenschaften durch ein einziges Array von Transaktionseigenschaften unter Verwendung einer Struktur zu ersetzen. Nun, Sie verwenden fast nichts von der Struktur (Lesen/Schreiben und Gleichsetzen). Der gleiche prozedurale Stil der Arbeit mit Transaktionseigenschaften.
Ich verwende die von Ihnen vorgeschlagenen Funktionen in den Artikeln über die Bibliothek. Das ist hier nicht notwendig.
//+------------------------------------------------------------------+ //|| Öffnet Datei zum Schreiben, gibt Handle zurück //+------------------------------------------------------------------+ bool FileOpenToWrite(int &handle) { ResetLastError(); handle=FileOpen(PATH, FILE_WRITE|FILE_BIN|FILE_COMMON); if(handle==INVALID_HANDLE) { PrintFormat("%s: FileOpen() failed. Error %d",__FUNCTION__, GetLastError()); return false; } //--- erfolgreich return true; } //+------------------------------------------------------------------+ //|| Öffnet Datei zum Lesen, gibt Handle zurück | //+------------------------------------------------------------------+ bool FileOpenToRead(int &handle) { ResetLastError(); handle=FileOpen(PATH, FILE_READ|FILE_BIN|FILE_COMMON); if(handle==INVALID_HANDLE) { PrintFormat("%s: FileOpen() failed. Error %d",__FUNCTION__, GetLastError()); return false; } //--- erfolgreich return true; } //+------------------------------------------------------------------+ //| Speichert die Transaktionsdaten aus dem Array in eine Datei //+------------------------------------------------------------------+ bool FileWriteDealsFromArray(SDeal &array[], ulong &file_size) { //--- wenn ein leeres Array übergeben wird - dies melden und false zurückgeben if(array.Size()==0) { PrintFormat("%s: Error! Empty deals array passed",__FUNCTION__); return false; } //--- öffne die Datei zum Schreiben, erhalte ihr Handle int handle=INVALID_HANDLE; if(!FileOpenToWrite(handle)) return false; //--- Verschieben des Dateizeigers an das Ende der Datei bool res=true; ResetLastError(); res&=FileSeek(handle, 0, SEEK_END); if(!res) PrintFormat("%s: FileSeek(SEEK_END) failed. Error %d",__FUNCTION__, GetLastError()); //--- Schreiben der Array-Daten an das Ende der Datei file_size=0; res&=(FileWriteArray(handle, array)==array.Size()); if(!res) PrintFormat("%s: FileWriteArray() failed. Error ",__FUNCTION__, GetLastError()); else file_size=FileSize(handle); //--- Schließen der Datei FileClose(handle); return res; } //+------------------------------------------------------------------+ //| Lädt Transaktionsdaten aus der Datei in das Array. //+------------------------------------------------------------------+ bool FileReadDealsToArray(SDeal &array[], ulong &file_size) { //--- öffne die Datei zum Lesen, erhalte ihr Handle int handle=INVALID_HANDLE; if(!FileOpenToRead(handle)) return false; //--- Verschieben des Dateizeigers an das Ende der Datei bool res=true; ResetLastError(); //--- Daten aus der Datei in ein Array lesen file_size=0; // res=(FileReadArray(handle, array)>0); res=(ArrayResize(array, FileReadArray(handle, array))>0); if(!res) PrintFormat("%s: FileWriteArray() failed. Error ",__FUNCTION__, GetLastError()); else file_size=FileSize(handle); //--- Schließen der Datei FileClose(handle); return res; }
Es gibt einen häufigen Fehler beim Hervorheben und Beheben.
Aber warum sollte man den ganzen Code nicht anders schreiben?
//+------------------------------------------------------------------+ //| Speichert die Transaktionsdaten aus dem Array in eine Datei //+------------------------------------------------------------------+ bool FileWriteDealsFromArray(SDeal &array[], long &file_size) { return((file_size = FileSave(PATH, array, FILE_COMMON) * sizeof(SDeal)) > 0); } //+------------------------------------------------------------------+ //| Lädt Transaktionsdaten aus der Datei in das Array. //+------------------------------------------------------------------+ bool FileReadDealsToArray2(SDeal &array[], long &file_size) { return((file_size = ArrayResize(array, (int)FileLoad(PATH, array, FILE_COMMON)) * sizeof(SDeal)) > 0); }
Der Artikel ist für ein relativ niedriges Ausbildungsniveau bestimmt. Er soll eine breite Leserschaft erreichen.
Forum zum Thema Handel, automatisierte Handelssysteme und Testen von Handelsstrategien
MetaTrader 5 Strategy Tester: Fehler, Bugs, Vorschläge zur Verbesserung seiner Arbeit
fxsaber, 2019.09.06 15:45
Eine gute Funktion, die Sie ignorieren
MqlTick tiks[]; if (FileLoad("deribit1.out.bin", ticks)) { // ....
- 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.
Neuer Artikel Visuelle Bewertung und Anpassung des Handels im MetaTrader 5 :
Stellen wir uns eine Situation vor: Auf einem Konto wird seit geraumer Zeit mehr oder weniger aktiv mit verschiedenen Instrumenten gehandelt, wobei verschiedene EAs und in einigen Fällen sogar manuell eingesetzt werden. Und jetzt, nach einiger Zeit, wollen wir die Ergebnisse all dieser Arbeit sehen. Natürlich können wir die Standard-Handelsberichte im Terminal durch Drücken der Tastenkombination Alt+E einsehen. Wir können auch Handelssymbole in unser Diagramm laden und Einstiegs- und Ausstiegszeiten für unsere Positionen sehen. Was aber, wenn wir die Dynamik unseres Handels sehen wollen, wo und wie Positionen eröffnet und geschlossen wurden? Wir können uns jedes Symbol einzeln oder alle auf einmal ansehen, einschließlich der Eröffnung und Schließung von Positionen, der Niveaus, auf denen Stop-Orders platziert wurden, und ob ihre Größe gerechtfertigt war. Wie wäre es, wenn wir uns die Frage stellen: „Was wäre passiert, wenn...“ (und hier gibt es viele Möglichkeiten – verschiedene Stopps, die Verwendung verschiedener Algorithmen und Kriterien, die Verwendung von Trailing-Positionen oder das Verschieben von Stopps zum Breakeven usw.) und dann alle unsere „Wenns“ mit einem klaren, sichtbaren Ergebnis testen. Wie könnte sich der Handel verändern, wenn...
Es stellt sich heraus, dass bereits alles vorhanden ist, um solche Probleme zu lösen. Alles, was wir tun müssen, ist, die Kontohistorie in eine Datei zu laden – alle abgeschlossenen Handelsgeschäfte – und dann einen EA im Strategietester laufen zu lassen, der Handelsgeschäfte aus der Datei liest und Positionen im Strategietester des Client-Terminals öffnet/schließt. Mit einem solchen EA können wir Code hinzufügen, um die Bedingungen für das Verlassen von Positionen zu ändern und zu vergleichen, wie sich der Handel verändert, und was passiert wäre, wenn...
Wie kann das für uns von Nutzen sein? Ein weiteres Instrument, um die besten Ergebnisse zu finden und Anpassungen am Handel vorzunehmen, der bereits seit einiger Zeit auf einem Konto läuft. Die visuelle Prüfung ermöglicht es uns, dynamisch zu sehen, ob die Positionen für ein bestimmtes Instrument korrekt geöffnet wurden, ob sie zum richtigen Zeitpunkt geschlossen wurden, usw. Und was am wichtigsten ist: Ein neuer Algorithmus kann einfach zum EA-Code hinzugefügt, getestet, die Ergebnisse ermittelt und die EAs, die auf diesem Konto arbeiten, angepasst werden.
Autor: Artyom Trishkin