
Visualisierung der Geschäfte auf dem Chart (Teil 1): Auswahl eines Zeitraums für die Analyse
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
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. 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. 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





- 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.