English Русский 中文 Español 日本語 Português
preview
Visualisierung der Geschäfte auf dem Chart (Teil 1): Auswahl eines Zeitraums für die Analyse

Visualisierung der Geschäfte auf dem Chart (Teil 1): Auswahl eines Zeitraums für die Analyse

MetaTrader 5Handelssysteme | 7 November 2024, 10:48
177 0
Aleksandr Seredin
Aleksandr Seredin

Einführung

In diesem Artikel werden wir ein Skript von Grund auf entwickeln, um Geschäfte (deals) bei der rückwirkenden Analyse von Handelsentscheidungen zu visualisieren. Einfach ausgedrückt: Wenn wir manuell handeln und unsere vergangenen Markteintritte analysieren, um unsere Handelsleistung zu verbessern, wollen wir so wenig Zeit wie möglich für die manuelle Analyse der Historie und die damit verbundene rein technische Arbeit aufwenden: Charts öffnen, in der Historie nach Geschäften suchen, Druckbilder manuell im Terminal speichern, selbstständig Stop Loss und Take Profit für abgeschlossene Geschäfte ziehen sowie Informationen über gezahlte Provisionen und Swaps sammeln. Das Skript, das wir hier entwickeln werden, wird dazu beitragen, die mechanische Arbeit erheblich zu reduzieren. Mit Hilfe des Skripts (das Sie aus den Anhängen unten zusammengestellt haben) können Sie die Zeit, die Sie für die technische Arbeit aufwenden, erheblich reduzieren, um mehr Zeit für die Analyse von Handelsentscheidungen zu haben. Diejenigen, die keine Zeit mit dem Zusammenstellen der Projekte verbringen möchten, können eine fertige Version des Skripts auf dem MQL5 Market herunterladen. Die Entwicklung des Skripts wird in zwei Teile aufgeteilt, wobei der Quellcode jedem Teil vollständig beigefügt wird.


Warum brauchen wir eine retrospektive Analyse?

Das Hauptziel eines jeden, der in den Markt einsteigt, ist es, einen langfristigen Gewinn bei kontrolliertem Risiko zu erzielen. Diese Art von Geschäft kann mit anderen verglichen werden, bei denen man auch mit Risiken umgehen muss. Wie die Praxis jedoch zeigt, gehen die meisten neuen Unternehmen in der Realwirtschaft schließlich in Konkurs. Auf den Finanzmärkten können die Gelder aufgrund des Einsatzes von Hebeln und der vollständigen Regression von Verlusten auf alle verfügbaren Gelder viel schneller verloren gehen. In der realen Wirtschaft können Sie, wenn etwas schief geht, Ihre Produktionsanlagen für andere Zwecke umwidmen (wenn sie nicht mit einer Hypothek belastet sind) und neu beginnen. Beim Handel betreffen alle Verluste Ihr gesamtes Konto.

Mehrere Statistiken bestätigen die These, dass Investitionen in die Finanzmärkte ohne Risikokontrolle und mit unangemessenem Einsatz von Hebelsätzen zu den gefährlichsten Kapitalanlagerisiken führen können. Nach einer Untersuchung der U.S. Securities and Exchange Commission (Freigabe Nr. 34-64874, Aktenzeichen: S7-30-11) über 17 Jahren:

„Etwa 70 % der Kunden verlieren jedes Quartal Geld, und im Durchschnitt sind 100 % der Investitionen eines Privatkunden in weniger als 12 Monaten verloren.

In der vorangegangenen Artikelserie über die Bedeutung des Risikoausgleichs und des Risikomanagements habe ich bereits darauf hingewiesen, dass unkontrolliertes Risiko immer zu Geldverlusten führt und selbst eine anfänglich profitable Strategie ohne Risikomanagement in eine unprofitable verwandelt werden kann. In dieser Artikelserie werden wir den Aspekt berücksichtigen, dass der Markt ein sehr flexibles Gebilde ist, das sich im Laufe der Zeit unter dem Einfluss verschiedener wirtschaftlicher und politischer Faktoren verändert. Zum besseren Verständnis können wir diese These vereinfachen, indem wir sagen, dass sich der Markt in mindestens zwei Phasen befinden kann - eine Seitwärtsbewegung und ein Trend. Daher ist es sehr wichtig, Ihre Handelsaktivitäten ständig zu analysieren, um festzustellen, ob die gewählte Strategie der Marktsituation angemessen ist.

Viele algorithmische Händler stellen fest, dass bestimmte Strategien in einem stagnierenden Markt gut funktionieren, aber anfangen, Geld zu verlieren, wenn sich der Markt bewegt. Ebenso verlieren Strategien, die an Trends angepasst sind, in einem stagnierenden Markt an Effizienz. Die Entwicklung von Algorithmen, die Marktphasenänderungen erkennen können, bevor Verluste die Gewinne auffressen, erfordert erhebliche Rechenressourcen und Zeit.

Diese Probleme zwingen die Händler, sich ständig zu fragen: „Mache ich alles richtig?“, „Ist der heutige Drawdown normal oder muss ich die Algorithmen anpassen?“, „Wie kann ich meine Ergebnisse verbessern?“ Die Antworten auf diese Fragen sind wichtig für den langfristigen Erfolg auf dem Markt. Die Methoden, um sie zu finden, sind unterschiedlich: Einige verwenden Strategieoptimierer, andere nutzen tiefe neuronale Netze, wieder andere stützen sich auf mathematische Modelle oder langjährige Erfahrung. Alle diese Ansätze können wirksam sein, da der Markt selbst der wichtigste Lehrmeister ist.

Die Beobachtung des Marktes wird oft zu einer Quelle innovativer Strategien und Investitionsideen. In diesem Artikel werden wir ein Skript erstellen, das nicht nur dazu beiträgt, die Effizienz des Handels zu verbessern, sondern auch neue Ideen für Algorithmen auf der Grundlage von Datenanalysen liefert. Die Analyse historischer Geschäfte ist für mich ein Muss, obwohl ich seit langem ausschließlich mit EAs handle.

Gehen wir nun dazu über, ein Skript zu schreiben, das den routinemäßigen Prozess der Analyse von Geschäften erheblich vereinfachen wird. Das Skript sollte uns schließlich Informationen in einem Druckbild ausgeben, wie in Abbildung 1 dargestellt.

Abbildung 1. Anzeige von Skriptdaten

Abbildung 1. Anzeige von Skriptdaten

Wir beginnen mit der Implementierung des Skripts, indem wir die Eingabedaten des Nutzers eingeben.


Skript-Eingaben

Die Skriptfunktionalität sollte die Möglichkeit bieten, Daten zu historischen Transaktionen automatisch auf einen Bildschirm zum Ausdrucken von Charts herunterzuladen, mit der Möglichkeit, mehrere Zeitrahmen für jedes Geschäft in einer separaten Datei festzulegen, sowie dem Nutzer die Option zu bieten, Daten zu einem einzelnen Geschäft oder zu allen Geschäften für einen vom Nutzer festgelegten Zeitraum herunterzuladen. Ich werde auch versuchen, die Grafiken auf den Druckbildern so weit wie möglich anzupassen.

Damit der Nutzer das Skript umschalten kann, um Daten für ein Geschäft oder für einen Zeitraum historischer Daten zu laden, müssen wir zunächst eine Enumeration, dem nutzerdefinierten Datentyp enum in der folgenden Form bereitstellen:

enum input_method
  {
   Select_one_deal,
   Select_period
  };

Die Implementierung unserer nutzerdefinierten Enumeration wird nur aus zwei angekündigten Optionen bestehen: der Wahl eines Geschäfts und der Wahl eines Zeitraums. Nun können wir mit der Deklaration der Eingabe auf der globalen Ebene der Eingabe der Klasse für das Speichern fortfahren:

input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

Zur Vereinfachung für den Nutzer haben wir hier einen benannten Block mit dem Schlüsselwort group bereitgestellt, um jeden wichtigen Parameter für den Nutzer visuell zu trennen und die notwendigen Erklärungen zu liefern. Kommentieren wir auch die Variablen der Eingaben der Speicherklasse aus, sodass die Variablennamen durch Textkommentare ersetzt werden, die für den durchschnittlichen Nutzer verständlich sind.

Auf der grafischen Oberfläche sieht die Eingabe der Variablenwerte wie in Abbildung 2 dargestellt aus.

Abbildung 2. Nutzeroberfläche für die Eingabe von Datenladebedingungen

Abbildung 2. Die Nutzeroberfläche für die Eingabe von Daten Ladebedingungen

Damit der Nutzer schnell Informationen über ein bestimmtes Geschäft erhalten kann, muss er die entsprechende Enumeration in der Variablen inp_method auf Select_one_deal setzen. Danach muss der Nutzer die Nummer (Ticket) des gewünschten Geschäfts angeben. Deklarieren Sie den Eingabeblock in der folgenden Form:

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

Legen wir einen Standardwert für die Variable fest, die dem Nutzer als Beispiel dienen kann, und wir geben vorsichtshalber an, dass es sich bei der Ticketnummer eines Auftrags um eine globale Nummer handelt. In der Regel zeigt das Terminal diese Nummer in der Geschäftshistorie an, sodass der Nutzer keine Schwierigkeiten mit diesem Problem haben sollte.

Wenn der Nutzer jedoch einen Zeitraum für die Analyse auswählen möchte, damit alle Geschäfte heruntergeladen werden, ist es notwendig, Eingaben zu machen, die dem Wert des Beginns und des Endes des Zeitraums der Auswahl von Geschäften in der Historie entsprechen. Dies kann durch den folgenden Eintrag auf globaler Ebene realisiert werden:

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

Der Nutzer gibt das Anfangsdatum der Stichprobe in die Variable start_date und das Enddatum des Stichprobenzeitraums in die Variable finish_date ein. Auch diese Variablen sollten mit Standardwerten initialisiert werden, um dem Nutzer die Arbeit zu erleichtern.

Nachdem wir nun Klarheit über die zu analysierenden Geschäfte haben, ist es an der Zeit, einen Mechanismus zu implementieren, der es dem Nutzer ermöglicht, Daten zu einem einzigen Geschäft in mehreren Charts zu speichern. Dies ist sehr praktisch, wenn der Händler beispielsweise beim manuellen Handel ein Tageschart verwendet, um die Handelsniveaus zu bestimmen, und auf M30-Charts nach einem Einstiegspunkt sucht. Es wird viel bequemer sein, historische Geschäfte zu analysieren, wenn unser Skript sofort einen Chart (sowohl M30 als auch D1) mit allen Daten herunterlädt.

Wir werden diese Idee auch durch Inputs umsetzen und nicht zwei, sondern vier Charts für die Handelsgeschäfte zur Verfügung stellen, um die Möglichkeiten des Nutzers zu erweitern, da einige Händler mehr als zwei Charts in ihrem Handel verwenden, aber nur sehr wenige mehr als vier. In Ausnahmefällen ist es möglich, das Skript mehrfach auszuführen. Zu diesem Zweck deklarieren wir vier Variablen des aufgezählten Standardtyps ENUM_TIMEFRAMES, wobei die Variable main_graph den Hauptladezeitrahmen bezeichnet, während die übrigen Variablen Hilfsvariablen sind. Schreiben wir den Code in der folgenden Form:

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

Die vom Nutzer gewählten Zeiträume können zeitlich sehr unterschiedlich sein, denn dafür sind sie in den obigen Einstellungen vorgesehen. Deshalb brauchen wir auch eine Chartverschiebung auf der rechten Seite, damit die Bilder ordentlich auf dem Chart positioniert werden, wie es für den Nutzer bequem ist. Das heißt, wenn wir das Druckbildauf dem Tages-Chart ausgeben, können wir einige Balken nach dem Geschäft sehen, die zeigen, wohin sich der Kurs nach dem Geschäft entwickelt hat. Für kleinere Zeiträume benötigen wir eine entsprechende Einstellung mit einem viel höheren Wert. Daher werden wir diese Verschiebungen in den Charts mit den Standardwerten wie im folgenden Code versehen.

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

Um den Chart so anzupassen, dass die Druckbilder im MetaTrader 5-Terminal gespeichert werden können, können wir die Funktionsweise der Templates verwenden oder alle Chart-Eigenschaften konfigurieren, indem wir den Chart-Operationscode implementieren und jeden Wert in einen separaten Skripteingang zuweisen. Ich glaube, dass es im Falle meiner Lösung eleganter und korrekter ist, den Abschnitt mit den Skripteingaben bei der Verwendung der Charteinstellungen nicht „aufzublasen“, sondern fertige Vorlagen für die Arbeit mit der Darstellung des Charts im Terminal zu verwenden. Als Ergebnis werden wir einfach den Namen einer vorbereiteten Vorlage in die Eingabe für jeden Zeitrahmen schreiben, und das Skript wird damit arbeiten. Sie können auch zuvor erstellte Vorlagen verwenden, um die Anzeige vertrauter zu machen.

Die Darstellung der Charts im MetaTrader 5-Terminal kann an nahezu jeden Geschmack und jede Vorliebe angepasst werden. Durch Drücken von F8 auf dem Chart können wir die Anzeigemodi, Objekte, Farbpalette und vieles mehr anpassen. Jede Charteinstellung kann schnell und bequem geändert werden, indem Vorlagen für verschiedene Anzeigekonfigurationen erstellt werden. Mit dem Kontextmenüpunkt Charts -> Vorlagen -> Vorlage speichern/laden können wir die Einstellung für die Darstellung des Preischarts schnell ändern, ohne das aktive Chartfenster zu wechseln. Infolgedessen lassen sich mehrere Einstellungen für die Chartdarstellung je nach Anzahl der analysierten Zeitrahmen in mehrere Variablen einteilen.

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

Die Nutzeroberfläche der Terminaleingänge sieht dann so aus wie in Abbildung 3 dargestellt:

Abbildung 3. Nutzerschnittstelle für Vorlageneingaben

Abbildung 3. Nutzeroberfläche von Vorlageneingaben

Nachdem wir uns nun für alle Standardeinstellungen entschieden haben, fügen wir nun die Einstellungen hinzu, die sich speziell auf die Anzeige der vollständigen Informationen zu den Geschäften beziehen, um dem Nutzer alle Informationen für die Analyse seiner Handelsoperationen zur Verfügung zu stellen. Dabei handelt es sich hauptsächlich um Objekte, die dem offenen Preis der Position, dem Stop Loss, dem Take Profit und der Verbindungslinie entsprechen. Die entsprechenden Farbvariablen vom Typ color sehen dann wie folgt aus:

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

Im Allgemeinen werden alle Eingaben unseres Skripts wie unten beschrieben aussehen:

#property copyright "Visit product page"
#property link      "https://www.mql5.com/ru/market/product/86223"
#property version   "1.00"
#property description "Make an automatic printscreen with a full description of all transactions for the period or 
			specify the ticket of the desired transaction."
#property script_show_inputs

enum input_method
  {
   Select_one_deal,
   Select_period
  };


input group "Enter deal ticket or select the period"
input input_method inp_method = Select_period;                          // One deal or period?

input group "For case 'Select_one_deal': Enter ticket of deal"
input long inp_d_ticket = 4339491;                                      // Ticket (global id)

input group "For case 'Select_period': Enter period for analys"
input datetime start_date = D'17.07.2023';                              // Start date
input datetime finish_date = D'19.07.2023';                             // Finish date

input group "Enter time frames. 'current' = don't use"
input ENUM_TIMEFRAMES main_graph = PERIOD_D1;                           // Period of main chart
input ENUM_TIMEFRAMES addition_graph = PERIOD_H1;                       // Period of addition chart
input ENUM_TIMEFRAMES addition_graph_2 = PERIOD_CURRENT;                // Period of addition chart #2
input ENUM_TIMEFRAMES addition_graph_3 = PERIOD_CURRENT;                // Period of addition chart #3

input group "Navigate settings in bars."
input int bars_from_right_main = 15;                                    // Shift from right on main chart
input int bars_from_right_add = 35;                                     // Shift from right on addition chart
input int bars_from_right_add_2 = 35;                                   // Shift from right on addition chart #2
input int bars_from_right_add_3 = 35;                                   // Shift from right on addition chart #3

input group "Properties of charts. Enter the template name:"
input string main_template = "dailyHistorytemp";                        // Template name of main chart
input string addition_template = "hourHistoryTemp";                     // Template name of addition chart
input string addition_template_2 = "hourHistoryTemp";                   // Template name of addition chart #2
input string addition_template_3 = "hourHistoryTemp";                   // Template name of addition chart #3

input group "Colors of deals line"
input color clr_price_open = clrWhiteSmoke;                             // Color of price open label
input color clr_price_close = clrWhiteSmoke;                            // Color of price close label
input color clr_stop = clrRed;                                          // Color of stop loss label
input color clr_take = clrLawnGreen;                                    // Color of take profit label
input color clr_main = clrWhiteSmoke;                                   // Color of deals trendline

Wir haben alle Variablen auf globaler Ebene definiert und können nun mit der Implementierung des Codes am Einstiegspunkt des Skripts in OnStart() fortfahren. Beginnen wir mit der Definition aller notwendigen Variablen für die Speicherung, Verarbeitung und Anzeige der Daten, die an die gespeicherte Druckbilddatei gesendet werden sollen. Bei der Bearbeitung des Skripts werden wir den Nutzer über jeden Schritt informieren.

Beginnen wir damit, den Nutzer darüber zu informieren, dass das Skript gestartet wurde, setzen wir die Fehlervariable zurück, damit wir den Fehlerrückgabecode korrekt überprüfen können, wenn etwas schief geht, und stellen wir Variablen für alle Positionseigenschaften bereit, sowie einen geeigneten Speicher für die Erfassung von Informationen über alle Geschäfte.

   Print("Script starts its work.");                                    // notified

   ResetLastError();                                                    // reset error

   string brok_name = TerminalInfoString(TERMINAL_COMPANY);             // get broker name
   long account_num = AccountInfoInteger(ACCOUNT_LOGIN);                // get account number

//---
   ulong    ticket = 0;                                                 // ticket
   ENUM_DEAL_ENTRY entry = -1;                                          // entry or exit
   long     position_id = 0,  PositionID[];                             // main id
   int      type = -1,        arr_type[];                               // deal type
   int      magic = -1,       arr_magic[];                              // magic number
   ENUM_DEAL_REASON      reason = -1,      arr_reason[];                // reason

   datetime time_open = 0,    arr_time_open[];                          // deal open time
   datetime time_close = 0,   arr_time_close[];                         // close time

   string   symbol,           arr_symbol[];                             // symbol
   string   comment,          arr_comment[];                            // comment
   string   externalID,       arr_extermalID[];                         // external id

   double   stop_loss = 0,    arr_stop_loss[];                          // deal Stop Loss
   double   take_profit = 0,  arr_take_profit[];                        // deal Take Profit
   double   open = 0,         arr_open[];                               // open price
   double   close = 0,        arr_close[];                              // close price
   double   volume = 0,       arr_volume[];                             // position volume
   double   commission = 0,   arr_commission[];                         // commission
   double   swap = 0,         arr_swap[];                               // swap
   double   profit = 0,       arr_profit[];                             // profit
   double   fee = 0,          arr_fee[];                                // fee

   int res = -1;                                                        // user command

Jetzt können wir die Erfassung von Geschäftsdaten davon abhängig machen, ob der Nutzer ein Druckbild für ein Geschäft oder für alle Geschäfte innerhalb eines bestimmten Zeitraums erhalten möchte.


Auswahl der historischen Daten nach einem Zeitraum

Implementieren wir die Nutzerauswahl durch den logischen Auswahloperator „switch“, der den Wert der eingegebenen globalen Variable inp_method abruft, und beginnen wir mit der Verarbeitung der Fallvariante Select_period, um Daten über abgeschlossene Geschäfte innerhalb eines bestimmten Zeitraums zu sammeln.

Informieren Sie den Nutzer zunächst darüber, dass in den Eingaben die Option der Analyse von Geschäften innerhalb eines Zeitraums gewählt wurde. Die Benachrichtigung wird mit der vordefinierten Funktion MessageBox() für den Aufruf des Nachrichtenfensters realisiert. Der dritte Parameter ist die Konstante MB_OKCANCEL, die es dem Terminal ermöglicht, die Skriptausführung zu unterbrechen, nachdem es auf „Abbrechen“ geklickt hat. Dies ist praktisch, da der Nutzer das Skript vorzeitig beenden und nicht auf seine Ausführung warten kann, wenn er versehentlich die falsche Option in die inp_method-Eingabe eingegeben hat. Der vollständige Code ist nachstehend aufgeführt.

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); // wait for user confirmation 

Wir werden das Ergebnis der Verarbeitung des Knopfdruck-Ereignisses der Variablen „res“ zuweisen, um den Mechanismus zur Unterbrechung des Skripts zu implementieren. Technisch gesehen ist es am einfachsten, das Skript durch den Operator return zu unterbrechen, wenn die Variable „res“ den Wert IDCANCEL enthält, was bedeutet, dass der Nutzer die entsprechende Schaltfläche gedrückt hat. Der Block wird durch den logischen Auswahloperator if in der folgenden Form dargestellt.

         if(res == IDCANCEL)                                            // if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); // notify
            return;                                                     // do not continue
           }

Wenn der Nutzer in diesem Stadium die Gültigkeit der Option bestätigt hat, beginnen das Erfassen der Informationen über abgeschlossene Geschäfte für den angegebenen historischen Zeitraum. Wir werden die Auswahl der historischen Geschäfte mit der vordefinierten Funktion HistorySelect() durchführen. Diese Funktion erhält die Werte für den Beginn und das Ende des Zeitraums, die der Nutzer oben eingegeben und bestätigt hat.

Nach der Abfrage von historischen Geschäften wäre es aus Gründen der Code-Optimierung und der Bequemlichkeit für den Nutzer sehr sinnvoll, eine Überprüfung des Vorhandenseins von Geschäften auf dem Konto innerhalb des vom Nutzer angegebenen Zeitraums vorzunehmen. Geben Sie die Anzahl der erhaltenen historischen Geschäfte über die vordefinierte Terminalfunktion HistoryDealsTotal() in die Variable „total“ ein:

            int total = HistoryDealsTotal();                            // got the total number of deals

Wenn es innerhalb des angegebenen Zeitraums nichts zu analysieren gibt und kein einziges Geschäft gefunden wurde, wird der Nutzer darüber informiert und das Skript gestoppt. Das Ereignis wird auch durch den logischen Operator if behandelt, in dem wir den Nutzer über das Fehlen von Geschäften innerhalb des angegebenen Zeitraums über das EA-Protokoll und das Informationsfenster informieren. Wir unterbrechen das Skript mit dem Operator return wie unten gezeigt:

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               return;
              }

Wenn innerhalb dieses Zeitraums ein Geschäft gefunden wurde, können wir mit der Datenerfassung beginnen. Wir iterieren über alle Geschäfte, die wir in der Vergangenheit erhalten haben, indem wir die for-Schleife wie unten gezeigt verwenden:

            for(int i=0; i<total; i++)                                  // iterate through the number of deals

Wir wählen die Daten für jedes einzelne historische Geschäft anhand seiner eindeutigen ID - Ticketnummer - aus und fordern sie mit der vordefinierten Terminalfunktion HistoryDealGetTicket() an. Deren Parameter empfangen Seriennummern von 0 bis „total“ und wir erhalten als Rückgabewert die eindeutige Geschäfts-ID wie unten dargestellt. Vergessen Sie nicht, die Gültigkeit des zurückgegebenen Wertes zu überprüfen.

               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the ticket

Nachdem Sie das Ticket für historische Geschäfte erhalten haben, fordern wir drei Hauptfunktionen an, die für die Erfassung allgemeiner Positionsdaten der Geschäfte erforderlich sind. Diese Funktionen umfassen: die ID einer Position, zu der ein historisches Geschäft gehört, das Merkmal ENUM_DEAL_ENTRY, das uns darüber informiert, was genau das Geschäft war, Eröffnung oder Schließung einer Position, und eine Geschäftsart mit dem Merkmal DEAL_TYPE, das die Auftragsrichtung und -art definiert. Alle drei Anfragen werden über die vordefinierte Terminalfunktion HistoryDealGetInteger() ausgeführt, wie unten dargestellt:

                  //--- get deals properties
                  position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID);        // took the main id
                  entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);   // entry or exit?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // deal type

Diese Daten werden in erster Linie deshalb angefordert, weil ihre Werte die Werte bestimmen, die wir für die Erfassung der Daten über den gesamten Posten verwenden werden. Es sei daran erinnert, dass die Daten zur allgemeinen Position aus einem Satz von Daten zu mehreren Aufträgen, die sich speziell auf diese Position beziehen, aggregiert und auf der Grundlage der Positions-ID miteinander verglichen werden.

Zunächst müssen alle historischen Transaktionen aussortiert werden, die nicht direkt mit den Handelsgeschäften zusammenhängen, wie z. B. die Auffüllung des Guthabens, die Abhebung von Geldern, die Gewährung von Boni durch den Broker usw. Nehmen Sie dazu die folgende Prüfung in den Code auf:

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )

Sobald wir uns vergewissert haben, dass es sich bei dem eingegangenen Geschäft um eine Handelsoperation handelt, d. h. um einen Kauf oder Verkauf, oder um die entsprechenden Abschlüsse, die je nach Art des Maklerkontos oder des Marktes variieren können, können wir mit der Erfassung von Daten über die Position als Ganzes beginnen. In Anbetracht der Besonderheiten der Speicherung von Positionsdaten in den entsprechenden Aufträgen werden wir einige Positionsmerkmale aus Aufträgen übernehmen, die sich auf die Eröffnung beziehen, während einige Merkmale aus Aufträgen entnommen werden, die sich auf die Schließung von Positionen beziehen. Zum Beispiel können Daten über das finanzielle Ergebnis einer Position erwartungsgemäß und logischerweise in den Aufträgen zur Positionsschließung gefunden werden. Um diese Daten anschaulicher und übersichtlicher darzustellen, werden sie in der folgenden Tabelle zusammengefasst:

# Eröffnung einer Position (DEAL_ENTRY_IN) Schließen einer Position (DEAL_ENTRY_OUT)
 1  open (DEAL_PRICE)  close (DEAL_PRICE)
 2  time_open (DEAL_TIME)  time_close (DEAL_TIME)
 3  symbol (DEAL_SYMBOL)  reason (DEAL_REASON)
 4  stop_loss (DEAL_SL)  swap (DEAL_SWAP)
 5  take_profit (DEAL_TP)  profit (DEAL_PROFIT)
 6  magic (DEAL_MAGIC)  fee (DEAL_FEE)
 7  comment (DEAL_COMMENT)  -
 8  externalID (DEAL_EXTERNAL_ID)  -
 9  volume (DEAL_VOLUME)  -
 10   commission (DEAL_COMMISSION)  -

Tabelle 1. Quellen für die Beschaffung von Daten über die gesamte Position je nach Art des Geschäfts

Im Code würde die Abfrage und Sortierung der in Tabelle 1 gezeigten Daten folgendermaßen aussehen:

                     if(entry == DEAL_ENTRY_IN)                         		// if this is an entry
                       {
                        open = HistoryDealGetDouble(ticket,DEAL_PRICE);                 // take open price
                        time_open  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);  // take open time
                        symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);   		// take symbol
                        stop_loss = HistoryDealGetDouble(ticket,DEAL_SL);  		// take Stop Loss
                        take_profit = HistoryDealGetDouble(ticket,DEAL_TP);		// take Take Profit

                        magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC);   	// take magic number
                        comment=HistoryDealGetString(ticket,DEAL_COMMENT);       	// take comment
                        externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);	// take external id
                        volume = HistoryDealGetDouble(ticket,DEAL_VOLUME);          	// take volume
                        commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);  	// take commission value
                       }

                     if(entry == DEAL_ENTRY_OUT)                        	 	// if this is an exit
                       {
                        close = HistoryDealGetDouble(ticket,DEAL_PRICE);               	// take close price
                        time_close  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);	// take close time

                        reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // 
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// fee
                       }

Nachdem die Positionsdaten vorläufig ermittelt wurden, sollte ein Container organisiert werden, in dem die relevanten Informationen gespeichert werden. Bei der Implementierung dieser Funktionalität werden wir standardmäßige eindimensionale Arrays für jedes Merkmal verwenden. Um das Vorhandensein einer Position im Speicher zu prüfen, wird eine kleine Find()-Schablonenfunktion definiert. Diese Funktion wird verwendet, um zu prüfen, ob eine Position im Container vorhanden ist. Die Logik ist, dass wir den Container und den Wert, den wir darin finden wollen, an die Funktionsparameter übergeben. Wie ich bereits erwähnt habe, suchen wir nach der ID der Position, zu der das Geschäft gehört. Wenn die Position gefunden wird, sollte die Funktion den entsprechenden Index zurückgeben. Wenn nicht, wird -1 zurückgegeben.

In Anbetracht der Tatsache, dass jede Eigenschaft einer einzelnen Position in verschiedenen Formaten als Zeichenkette, ganze oder reelle Zahl gespeichert werden muss, ist es sinnvoll, die Funktion Find() über eine Vorlage als überladbar zu deklarieren. Die Programmiersprache MQL5 ermöglicht es uns, diese Funktionalität mit Hilfe des Schlüsselworts Template flexibel und sehr bequem zu implementieren. Dadurch können wir unsere Funktionsvorlage mit dem überladbaren Datentyp typename einmal deklarieren, während der Compiler automatisch die erforderliche Implementierung für jeden Datentyp ersetzt. Da wir dort keine nutzerdefinierten Datentypen übergeben werden, gibt es keine Probleme mit dem impliziten Casting verschiedener Typen, und es besteht keine Notwendigkeit, Operatorüberladungen vorzunehmen. Die Implementierung der Vorlage für die nutzerdefinierte Funktion Find() ist unten dargestellt.

template<typename A>
int               Find(A &aArray[],A aValue)
  {
   for(int i=0; i<ArraySize(aArray); i++)
     {
      if(aArray[i]==aValue)
        {
         return(i);                                                     // The element exists, return the element index
        }
     }
   return(-1);                                                          // No such element, return -1
  }

Vervollständigen wir die Logik unter Verwendung der deklarierten Find()-Funktionsvorlage, indem wir prüfen, ob sich die aktuelle Position im Speicher befindet. Wenn die Funktion -1 zurückgegeben hat, gibt es keine Position im Speicher und sie muss dort hinzugefügt werden. Die Speichergröße sollte zuerst geändert werden:

                     //--- enter data into the main storage
                     //--- check if there is such id
                     if(Find(PositionID,position_id)==-1)               // if there is no such deal yet,                       {

Wenn eine solche Nummer im Speicher vorhanden ist, erhalten wir über den von der Funktion Find() zurückgegebenen Index Zugriff auf die Positionsdaten. Dies ist darauf zurückzuführen, dass sich die Aufträge in der Handelshistorie in verschiedenen Aufträgen befinden können, wenn mehrere Instrumente gleichzeitig auf dem Konto gehandelt werden. So kann beispielsweise eine Position in einem Instrument später eröffnet werden als ein früherer Auftrag in einem anderen Instrument. Dementsprechend kann sie früher geschlossen werden als die erste Symbolposition. Die Logik der Suche und Erfassung von Informationen über Positionen für den Zeitraum wird im Folgenden allgemein dargestellt und zusammengefasst.

      case  Select_period:                                              			// search within a period

         res = MessageBox("You have selected analysis for period. Continue?","",MB_OKCANCEL); 	// wait for user confirmation

         if(res == IDCANCEL)                                            			// if interrupted by user
           {
            printf("%s - %d -> Scrypt was stoped by user.",__FUNCTION__,__LINE__); 		// notify
            return;                                                     			// stop
           }

         MessageBox("Please press 'Ok' and wait for the next message until script will be done."); // notify

         //--- select history data
         if(HistorySelect(start_date,finish_date))                      // select the necessary period in history
           {
            int total = HistoryDealsTotal();                            // got the total number of deals

            if(total <= 0)                                              // if nothing found
              {
               printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__); // notify
               MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
               return;
              }

            for(int i=0; i<total; i++)                                  // iterate through the number of deals
              {
               //--- try to get deals ticket
               if((ticket=HistoryDealGetTicket(i))>0)                   // took the ticket
                 {
                  //--- get deals properties
                  position_id = HistoryDealGetInteger(ticket,DEAL_POSITION_ID);        // took the main id
                  entry = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(ticket,DEAL_ENTRY);   // entry or exit?
                  type = (int)HistoryDealGetInteger(ticket,DEAL_TYPE);  	       // entry or exit?

                  //--- check the deal type
                  if(type == DEAL_TYPE_BUY            ||                // if it is buy
                     type == DEAL_TYPE_SELL           ||                // if it is sell
                     type == DEAL_TYPE_BUY_CANCELED   ||                // if canceled buy
                     type == DEAL_TYPE_SELL_CANCELED                    // if canceled sell
                    )
                    {
                     //--- is it entry or exit?
                     if(entry == DEAL_ENTRY_IN)                         // if this is an entry
                       {
                        open = HistoryDealGetDouble(ticket,DEAL_PRICE);                	// take open price
                        time_open  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME); 	// take open time
                        symbol=HistoryDealGetString(ticket,DEAL_SYMBOL);   		// take symbol
                        stop_loss = HistoryDealGetDouble(ticket,DEAL_SL);  		// take Stop Loss
                        take_profit = HistoryDealGetDouble(ticket,DEAL_TP);		// take Take Profit

                        magic = (int)HistoryDealGetInteger(ticket,DEAL_MAGIC);   	// take magic number
                        comment=HistoryDealGetString(ticket,DEAL_COMMENT);       	// take comment
                        externalID=HistoryDealGetString(ticket,DEAL_EXTERNAL_ID);	// take external id
                        volume = HistoryDealGetDouble(ticket,DEAL_VOLUME);          	// take volume
                        commission = HistoryDealGetDouble(ticket,DEAL_COMMISSION);  	// take commission value
                       }

                     if(entry == DEAL_ENTRY_OUT)                        		// if this is an exit
                       {
                        close = HistoryDealGetDouble(ticket,DEAL_PRICE);               	// take close price
                        time_close  =(datetime)HistoryDealGetInteger(ticket,DEAL_TIME);	// take close time

                        reason = (ENUM_DEAL_REASON)HistoryDealGetInteger(ticket,DEAL_REASON); // reason
                        swap = HistoryDealGetDouble(ticket,DEAL_SWAP);        		// swap
                        profit = HistoryDealGetDouble(ticket,DEAL_PROFIT);    		// profit
                        fee = HistoryDealGetDouble(ticket,DEAL_FEE);          		// fee
                       }


                     //--- enter data into the main storage
                     //--- check if there is such id
                     if(Find(PositionID,position_id)==-1)               		// if there is no such deal yet,
                       {
                        //--- change the size of containers
                        ArrayResize(arr_time_open,ArraySize(arr_time_open)+1);       
                        ArrayResize(arr_time_close,ArraySize(arr_time_close)+1);     
                        ArrayResize(arr_symbol,ArraySize(arr_symbol)+1);             
                        ArrayResize(arr_stop_loss,ArraySize(arr_stop_loss)+1);       
                        ArrayResize(arr_take_profit,ArraySize(arr_take_profit)+1);   
                        ArrayResize(arr_open,ArraySize(arr_open)+1);                 
                        ArrayResize(arr_close,ArraySize(arr_close)+1);               
                        ArrayResize(PositionID,ArraySize(PositionID)+1);             

                        ArrayResize(arr_magic,ArraySize(arr_magic)+1);               
                        ArrayResize(arr_extermalID,ArraySize(arr_extermalID)+1);     
                        ArrayResize(arr_comment,ArraySize(arr_comment)+1);           
                        ArrayResize(arr_volume,ArraySize(arr_volume)+1);             
                        ArrayResize(arr_commission,ArraySize(arr_commission)+1);     
                        ArrayResize(arr_reason,ArraySize(arr_reason)+1);             
                        ArrayResize(arr_swap,ArraySize(arr_swap)+1);                 
                        ArrayResize(arr_profit,ArraySize(arr_profit)+1);             
                        ArrayResize(arr_fee,ArraySize(arr_fee)+1);                   

                        PositionID[ArraySize(arr_time_open)-1]=position_id;          


                        if(entry == DEAL_ENTRY_IN)                      		  // if this is an entry,
                          {
                           arr_time_open[    ArraySize(arr_time_open)-1]   = time_open;   // deal time
                           arr_symbol[       ArraySize(arr_symbol)-1]      = symbol;      // instrument symbol
                           arr_stop_loss[    ArraySize(arr_stop_loss)-1]   = stop_loss;   // deal Stop Loss
                           arr_take_profit[  ArraySize(arr_take_profit)-1] = take_profit; // deal Take Profit
                           arr_open[         ArraySize(arr_open)-1]        = open;        // open price
                           //---
                           arr_magic[        ArraySize(arr_magic)-1]       = magic;       // magic number
                           arr_comment[      ArraySize(arr_comment)-1]     = comment;     // comment
                           arr_extermalID[   ArraySize(arr_extermalID)-1]  = externalID;  // external id
                           arr_volume[       ArraySize(arr_volume)-1]      = volume;      // volume
                           arr_commission[   ArraySize(arr_commission)-1]  = commission;  // commission
                          }

                        if(entry == DEAL_ENTRY_OUT)                     		  // if this is an exit
                          {
                           arr_time_close[   ArraySize(arr_time_close)-1]  = time_close;  // close time
                           arr_close[        ArraySize(arr_close)-1]       = close;       // close price
                           //---
                           arr_reason[       ArraySize(arr_reason)-1]      = reason;      // reason
                           arr_swap[         ArraySize(arr_swap)-1]        = swap;        // swap
                           arr_profit[       ArraySize(arr_profit)-1]      = profit;      // profit
                           arr_fee[          ArraySize(arr_fee)-1]         = fee;         // fee
                          }
                       }
                     else
                       {
                        int index = Find(PositionID,position_id);       // if found, search for the index

                        if(entry == DEAL_ENTRY_IN)                      // if this is an entry
                          {
                           arr_time_open[index]   = time_open;          // deal time
                           arr_symbol[index]      = symbol;             // symbol
                           arr_stop_loss[index]   = stop_loss;          // deal Stop Loss
                           arr_take_profit[index] = take_profit;        // deal Take Profit
                           arr_open[index]        = open;               // close price
                           //---
                           arr_magic[index]       = magic;              // magic number
                           arr_comment[index]     = comment;            // comment
                           arr_extermalID[index]  = externalID;         // external id
                           arr_volume[index]      = volume;             // volume
                           arr_commission[index]  = commission;         // commission
                          }

                        if(entry == DEAL_ENTRY_OUT)                     // if this is an exit
                          {
                           arr_time_close[index]  = time_close;         // deal close time
                           arr_close[index]       = close;              // deal close price
                           //---
                           arr_reason[index]      = reason;             // reason
                           arr_swap[index]        = swap;               // swap
                           arr_profit[index]      = profit;             // profit
                           arr_fee[index]         = fee;                // fee
                          }
                       }
                    }
                 }
              }
           }
         else
           {
            printf("%s - %d -> Error of selecting history deals: %d",__FUNCTION__,__LINE__,GetLastError()); // notify
            printf("%s - %d -> No deals were found for the specified period.",__FUNCTION__,__LINE__);       // notify
            MessageBox("No deals were found for the specified period: "+TimeToString(start_date)+"-"+TimeToString(finish_date)+". Script is done.");
            return;
           }
         break;


Zusammenfassung des ersten Teils

In diesem Artikel haben wir die Bedeutung der historischen Handelsanalyse für einen sicheren und langfristigen Handel an den Finanzmärkten betrachtet. Ein Schlüsselelement dieser Analyse ist das Studium historischer Geschäfte, mit dessen Umsetzung wir im Drehbuch begonnen haben. Wir haben die Bildung von Inputs und die Implementierung des Algorithmus für die Auswahl historischer Daten über Geschäfte innerhalb des gewählten Zeitraums berücksichtigt. Wir haben auch eine überladbare Funktionsvorlage implementiert, um den Umgang mit Datencontainern zu vereinfachen.

Im nächsten Artikel werden wir das Skript vervollständigen und dabei den Algorithmus für die Auswahl von Daten für ein einzelnes Geschäft sowie das Zeichnen von Charts und die Implementierung des Codes für die Anzeige von Datenobjekten in Charts berücksichtigen.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/14903

Beigefügte Dateien |
DealsPrintScreen.mq5 (104.52 KB)
Nachrichtenhandel leicht gemacht (Teil 2): Risikomanagement Nachrichtenhandel leicht gemacht (Teil 2): Risikomanagement
In diesem Artikel wird die Vererbung in unseren bisherigen und neuen Code eingeführt. Um die Effizienz zu erhöhen, wird ein neues Datenbankdesign eingeführt. Darüber hinaus wird eine Risikomanagementklasse eingerichtet, die sich mit der Berechnung des Volumens befasst.
Entwicklung eines Expertenberaters für mehrere Währungen (Teil 12): Entwicklung eines Risikomanagers auf der Ebene des Eigenhandels Entwicklung eines Expertenberaters für mehrere Währungen (Teil 12): Entwicklung eines Risikomanagers auf der Ebene des Eigenhandels
In dem EA, der hier entwickelt wird, haben wir bereits einen bestimmten Mechanismus zur Kontrolle des Drawdowns. Sie ist jedoch probabilistischer Natur, da sie auf den Ergebnissen von Tests mit historischen Preisdaten beruht. Daher kann der Drawdown manchmal die maximal erwarteten Werte übersteigen (wenn auch mit einer geringen Wahrscheinlichkeit). Versuchen wir, einen Mechanismus hinzuzufügen, der die garantierte Einhaltung der festgelegten Drawdown-Höhe gewährleistet.
Neuronale Netze leicht gemacht (Teil 90): Frequenzinterpolation von Zeitreihen (FITS) Neuronale Netze leicht gemacht (Teil 90): Frequenzinterpolation von Zeitreihen (FITS)
Durch die Untersuchung der FEDformer-Methode haben wir die Tür zum Frequenzbereich der Zeitreihendarstellung geöffnet. In diesem neuen Artikel werden wir das begonnene Thema fortsetzen. Wir werden uns mit einer Methode befassen, mit der wir nicht nur eine Analyse durchführen, sondern auch spätere Zustände in einem bestimmten Bereich vorhersagen können.
Neuronales Netz in der Praxis: Geradenfunktion Neuronales Netz in der Praxis: Geradenfunktion
In diesem Artikel werden wir einen kurzen Blick auf einige Methoden werfen, um eine Funktion zu erhalten, die unsere Daten in der Datenbank darstellen kann. Ich werde nicht im Detail darauf eingehen, wie man Statistiken und Wahrscheinlichkeitsstudien zur Interpretation der Ergebnisse verwendet. Überlassen wir das denjenigen, die sich wirklich mit der mathematischen Seite der Angelegenheit befassen wollen. Die Erforschung dieser Fragen wird entscheidend sein für das Verständnis dessen, was bei der Untersuchung neuronaler Netze eine Rolle spielt. Hier werden wir dieses Thema in aller Ruhe besprechen.