English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
preview
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 12): Times and Trade (I)

Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 12): Times and Trade (I)

MetaTrader 5Handelssysteme | 6 Juli 2022, 10:20
250 0
Daniel Jose
Daniel Jose

Einführung

„Tape Reading“ (Lesen des Datenstroms) ist eine Handelsmethode, die von einigen Händlern in verschiedenen Phasen des Handels verwendet wird. Diese Methode ist sehr effektiv und sorgt bei richtiger Anwendung für ein stabiles Gewinnwachstum auf sicherere und konsistentere Weise als die Verwendung der bekannten Price Action, bei der es sich um eine reine Beobachtung der Kerzen handelt. Allerdings ist die Anwendung von Tape Reading in seiner derzeit vorgestellten Form ein sehr komplexer und langwieriger Prozess, der ständige Konzentration erfordert. Mit der Zeit machen wir unweigerlich Beobachtungsfehler.

Das Problem beim Bandlesen hängt mit der Menge an Informationen zusammen, die wir analysieren müssen. Schauen wir uns einen typischen Anwendungsfall für Tape Reading an:


Das eigentliche Problem ist, dass wir während der Analyse auf den Preis schauen müssen und wie er sich verändert, aber die Überprüfung dieser Werte bei Mini-Kontrakten ist nicht sehr praktisch. Daher betrachten wir in der Regel nicht den Inhalt des Datenstroms in Mini-Kontrakten, sondern ziehen es vor, vollständige Kontrakte zu beobachten, da sie diejenigen sind, die den Markt bewegen. Dies ist, was tatsächlich passiert, also sieht das System wie das folgende aus. Es ist etwas einfacher zu interpretieren und zu befolgen.


Aber selbst in diesem Fall ist die Anwendung des Systems ein sehr langwieriger Prozess, der äußerste Aufmerksamkeit erfordert. Die Situation wird noch enger, wenn Stopp-Positionen aktiviert sind, und in diesem Fall können wir einen Teil der Bewegung verpassen, da das Scrollen der Informationen auf dem Bildschirm sehr schnell sein sollte.


Planung

Die MetaTrader 5-Plattform verfügt jedoch auch für Mini-Kontrakte über ein alternatives System, das die Überwachung viel effizienter und einfacher macht. Mal sehen, wie es bei der Arbeit mit Mini-Kontrakten aussieht:

Wie Sie sehen können, ist die Interpretation viel einfacher. Aus den zuvor besprochenen Gründen ist es jedoch angemessener, vollständige Verträge zu verwenden, sodass es wie folgt aussieht:


Beachten Sie, dass die Handelsdaten durch das Rauschen der BID- und ASK-Bewegungen behindert werden. Die Deals werden hier als Kreise dargestellt. Rot zeigt Verkaufsgeschäfte, Blaue sind Kaufgeschäfte und Grün zeigt Direkt-Aufträge. Neben der Tatsache, dass wir Informationen haben, die für die Beobachtung selbst nicht benötigt werden, haben wir ein weiteres Problem: Das System ist von dem Chart getrennt, mit dem wir eigentlich handeln, wodurch wir zwei Bildschirme überwachen müssen. Das ist einerseits ein Vorteil, verkompliziert die Sache aber in manchen Fällen enorm. Also schlage ich hier vor, ein System zu schaffen, das einfach zu lesen ist und es uns gleichzeitig ermöglicht, diesen Indikator direkt auf dem Trading-Chart zu sehen.


Umsetzung

Das erste, was wir tun werden, ist, die Klasse C_Terminal so zu ändern, dass wir auf das gesamte Vertragsvermögen zugreifen können, und dies geschieht durch Hinzufügen des folgenden Codes:

void CurrentSymbol(void)
{
        MqlDateTime mdt1;
        string sz0, sz1, sz2;
        datetime dt = TimeLocal();
                
        sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
        m_Infos.szFullSymbol = _Symbol;
        m_Infos.TypeSymbol = ((sz0 == "WDO") || (sz0 == "DOL") ? WDO : ((sz0 == "WIN") || (sz0 == "IND") ? WIN : OTHER));
        if ((sz0 != "WDO") && (sz0 != "DOL") && (sz0 != "WIN") && (sz0 != "IND")) return;
        sz2 = (sz0 == "WDO" ? "DOL" : (sz0 == "WIN" ? "IND" : sz0));
        sz1 = (sz2 == "DOL" ? "FGHJKMNQUVXZ" : "GJMQVZ");
        TimeToStruct(TimeLocal(), mdt1);
        for (int i0 = 0, i1 = mdt1.year - 2000;;)
        {
                m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1);
                m_Infos.szFullSymbol = StringFormat("%s%s%d", sz2, StringSubstr(sz1, i0, 1), i1);
                if (i0 < StringLen(sz1)) i0++; else
                {
                        i0 = 0;
                        i1++;
                }
                if (macroGetDate(dt) < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_EXPIRATION_TIME))) break;
        }
}

// ... Class code ...

inline string GetFullSymbol(void) const { return m_Infos.szFullSymbol; }


Durch Hinzufügen der hervorgehobenen Zeilen haben wir Zugriff auf den gewünschten Vermögenswert, den wir in unserem Time & Trade-Programm verwenden werden. Als Nächstes können wir mit der Erstellung einer Objektklasse fortfahren, die unser Time & Trade unterstützt. Diese Klasse wird einige sehr interessante Funktionen enthalten. Zuerst muss ein Unterfenster erstellt werden, das unseren Indikator enthält. Es ist einfach, aber aus praktischen Gründen werden wir das Unterfenstersystem, das wir zuvor verwendet haben, nicht verwenden. Vielleicht ändert sich das Konzept in Zukunft, aber vorerst arbeiten wir mit Time & Trade in einem separaten Fenster vom Kennzahlensystem, was viel Vorarbeit bedeutet.

Beginnen wir mit der Erstellung einer neuen Unterstützungsdatei, um dem Indikator einen anderen Namen zu geben. Anstatt Dateien über Dateien zu erstellen, machen wir etwas Eleganteres. Wir ändern die Support-Datei, um mehr Möglichkeiten zu haben. Die neue Support-Datei wird unten angezeigt:

#property copyright "Daniel Jose 07-02-2022 (A)"
#property version   "1.00"
#property description "This file only serves as supporting indicator for SubWin"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
input string user01 = "SubSupport";             //Short Name
//+------------------------------------------------------------------+
int OnInit()
{
        IndicatorSetString(INDICATOR_SHORTNAME, user01);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+


Ich habe die Änderungen hervorgehoben, die an der Quelldatei vorgenommen werden sollten. Jetzt müssen wir Änderungen an unserem EA-Code vornehmen. Wir werden jetzt eine neue Klasse erstellen:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Terminal.mqh"
//+------------------------------------------------------------------+
class C_FnSubWin
{
        private :
                string  m_szIndicator;
                int             m_SubWin;
//+------------------------------------------------------------------+
                void Create(const string szIndicator)
                        {
                                int i0;
                                m_szIndicator = szIndicator;
                                if ((i0 = ChartWindowFind(Terminal.Get_ID(), szIndicator)) == -1)
                                        ChartIndicatorAdd(Terminal.Get_ID(), i0 = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WINDOWS_TOTAL), iCustom(NULL, 0, "::" + def_Resource, szIndicator));
                                m_SubWin = i0;
                        }
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                C_FnSubWin()
                        {
                                m_szIndicator = NULL;
                                m_SubWin = -1;
                        }
//+------------------------------------------------------------------+
                ~C_FnSubWin()
                        {
                                Close();
                        }
//+------------------------------------------------------------------+
                void Close(void)
                        {
                                if (m_SubWin >= 0) ChartIndicatorDelete(Terminal.Get_ID(), m_SubWin, m_szIndicator);
                                m_SubWin = -1;
                        }
//+------------------------------------------------------------------+
inline int GetIdSubWinEA(const string szIndicator = NULL)
                        {
                                if ((szIndicator != NULL) && (m_SubWin < 0)) Create(szIndicator);
                                return m_SubWin;
                        }
//+------------------------------------------------------------------+
inline bool ExistSubWin(void) const { return m_SubWin >= 0; }
//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+


Diese Klasse ersetzt C_SubWindow und unterstützt jetzt die Erstellung von Unterfenstern in einem Diagramm. Um zu verstehen, wie diese Klasse funktioniert, werfen Sie einen kurzen Blick auf die neue Klasse C_SubWindow unten:

#include "C_ChartFloating.mqh"
#include <NanoEA-SIMD\Auxiliar\C_FnSubWin.mqh>
//+------------------------------------------------------------------+
class C_SubWindow : public C_ChartFloating
{
//+------------------------------------------------------------------+
        private :
                C_FnSubWin      m_fnSubWin;
//+------------------------------------------------------------------+
        public  :
//+------------------------------------------------------------------+
                ~C_SubWindow()
                        {
                                Close();
                        }       
//+------------------------------------------------------------------+
                void Close(void)
                        {
                                m_fnSubWin.Close();
                                CloseAlls();
                        }
//+------------------------------------------------------------------+
inline int GetIdSubWinEA(void)
                        {
                                return m_fnSubWin.GetIdSubWinEA("SubWinSupport");
                        }
//+------------------------------------------------------------------+
inline bool ExistSubWin(void) const { return m_fnSubWin.ExistSubWin(); }
//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+


Beachten Sie, dass die Klasse die Definition des Indikators enthält, der zur Unterstützung von Vorlagen verwendet wird. Es ist im obigen Code hervorgehoben. Jetzt kommt der heikle Teil. Wenn wir anstelle von SubWinSupport einen anderen Namen verwenden, sucht die Klasse C_FnSubWin nach einem anderen Indikator. Wir werden diesen Trick anwenden, um das Erstellen von Indikatordateien zu vermeiden. Wir teilen der Klasse C_FnSubWin einfach mit, wie der Kurzname des gewünschten Indikators lauten soll. Daher sind wir nicht durch die Anzahl unnötiger Unterfenster oder Indikatordateien beschränkt, die nur zum Erstellen eines Expert Advisor-Unterfensters verwendet werden.

Danach können wir mit der Erstellung der Klasse C_TimeAndTrade fortfahren.


Die C_TimesAndTrade-Klasse

Die Objektklasse C_TimesAndTrade besteht aus mehreren kleinen Teilen, von denen jedes für etwas Bestimmtes verantwortlich ist. Der unten gezeigte Code ist das erste, was der EA für diese Klasse aufruft:

void Init(const int iScale = 2)
{
        if (!ExistSubWin())
        {
                CreateCustomSymbol();
                CreateChart();
        }
        ObjectSetInteger(Terminal.Get_ID(), m_szObjName, OBJPROP_CHART_SCALE, (iScale > 5 ? 5 : (iScale < 0 ? 0 : iScale)));
}

Dieser Code prüft, ob das Support-Unterfenster vorhanden ist. Wenn es noch nicht existiert, wird der Code eines erstellen. Werfen wir nun einen Blick auf den folgenden Code der anfänglichen Unterstützung für die Klasse:

inline void CreateCustomSymbol(void)
{
        m_szCustomSymbol = "_" + Terminal.GetFullSymbol();
        SymbolSelect(Terminal.GetFullSymbol(), true);
        SymbolSelect(m_szCustomSymbol, false);
        CustomSymbolDelete(m_szCustomSymbol);
        CustomSymbolCreate(m_szCustomSymbol, StringFormat("Custom\\Robot\\%s", m_szCustomSymbol), Terminal.GetFullSymbol());
        CustomRatesDelete(m_szCustomSymbol, 0, LONG_MAX);
        CustomTicksDelete(m_szCustomSymbol, 0, LONG_MAX);
        SymbolSelect(m_szCustomSymbol, true);
};

Dieser Code erstellt ein nutzerdefiniertes Symbol und setzt alle Daten innerhalb dieses Symbols zurück. Um die Anzeige von Symbolinhalten in dem Fenster zu ermöglichen, das wir erstellen werden, sollte dieses Symbol zuerst zu Market Watch hinzugefügt werden. Dies geschieht im folgenden Code:

SymbolSelect(m_szCustomSymbol, true);

Das nutzerdefinierte Symbol wird unter Custom\Robot <Symbolname> erstellt.. Seine Anfangsdaten werden durch das ursprüngliche Symbol bereitgestellt. Es ist im folgenden Code implementiert:

CustomSymbolCreate(m_szCustomSymbol, StringFormat("Custom\\Robot\\%s", m_szCustomSymbol), Terminal.GetFullSymbol());

Im Grunde ist das alles. Fügen wir die Klasse zum EA hinzu und führen Sie sie wie folgt aus:

// ... Expert Advisor code

#include <NanoEA-SIMD\Tape Reading\C_TimesAndTrade.mqh>

// ... Expert Advisor code

input group "Times & Trade"
input   int     user041 = 2;    //Escala
//+------------------------------------------------------------------+
C_TemplateChart Chart;
C_WallPaper     WallPaper;
C_VolumeAtPrice VolumeAtPrice;
C_TimesAndTrade TimesAndTrade;
//+------------------------------------------------------------------+
int OnInit()
{
// ... Expert Advisor code

        TimesAndTrade.Init(user041);
        
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}


Das Ergebnis ist folgendes:


Und es ist genau das, was erwartet wurde. Fügen wir nun die Werte der durchgeführten Geschäfte zum _DOLH22-Diagramm hinzu. Dieses Chart spiegelt alle durchgeführten Geschäfte wider, um eine grafische Darstellung von Times & Trade bereitzustellen. Die Präsentation erfolgt in Form japanischer Kerzenmuster, da sie einfach zu verwenden sind. Zuvor müssen wir einige Dinge tun, insbesondere das Symbol verbinden und synchronisieren. Dies geschieht im folgenden Code:

inline void Connect(void)
{
        switch (m_ConnectionStatus)
        {
                case 0:
                        if (!TerminalInfoInteger(TERMINAL_CONNECTED)) return; else m_ConnectionStatus = 1;
                case 1:
                        if (!SymbolIsSynchronized(Terminal.GetFullSymbol())) return; else m_ConnectionStatus = 2;
                case 2:
                        m_LastTime = TimeLocal();
                        m_MemTickTime = macroMinusMinutes(60, m_LastTime) * 1000;
                        m_ConnectionStatus = 3;
                default:
                        break;
        }
}

Die Funktion prüft, ob das Terminal verbunden ist und synchronisiert dann das Symbol. Danach können wir damit beginnen, Werte zu erfassen und auf dem Bildschirm anzuzeigen. Dafür ist aber eine kleine Änderung am Initialisierungscode notwendig. Die Änderung wird im folgenden Code hervorgehoben:

void Init(const int iScale = 2)
{
        if (!ExistSubWin())
        {
                CreateCustomSymbol();
                CreateChart();
                m_ConnectionStatus = 0;
        }
        ObjectSetInteger(Terminal.Get_ID(), m_szObjName, OBJPROP_CHART_SCALE, (iScale > 5 ? 5 : (iScale < 0 ? 0 : iScale)));
}

Danach können wir die Capture-Funktion sehen.

inline void Update(void)
{
        MqlTick Tick[];
        MqlRates Rates[def_SizeBuff];
        int i0, p1, p2 = 0;
        int iflag;

        if (m_ConnectionStatus < 3) return;
        if ((i0 = CopyTicks(Terminal.GetFullSymbol(), Tick, COPY_TICKS_ALL, m_MemTickTime, def_SizeBuff)) > 0)
        {
                for (p1 = 0, p2 = 0; (p1 < i0) && (Tick[p1].time_msc == m_MemTickTime); p1++);
                for (int c0 = p1, c1 = 0; c0 < i0; c0++)
                {
                        if (Tick[c0].volume == 0) continue;
                        iflag = 0;
                        iflag += ((Tick[c0].flags & TICK_FLAG_BUY) == TICK_FLAG_BUY ? 1 : 0);
                        iflag -= ((Tick[c0].flags & TICK_FLAG_SELL) == TICK_FLAG_SELL ? 1 : 0);
                        if (iflag == 0) continue;
                        Rates[c1].high = Tick[c0].ask;
                        Rates[c1].low = Tick[c0].bid;
                        Rates[c1].open = Tick[c0].last;
                        Rates[c1].close = Tick[c0].last + ((Tick[c0].volume > 200 ? 200 : Tick[c0].volume) * (Terminal.GetTypeSymbol() == C_Terminal::WDO ? 0.02 : 1.0) * iflag);
                        Rates[c1].time = m_LastTime;
                        p2++;
                        c1++;
                        m_LastTime += 60;
                }
                CustomRatesUpdate(m_szCustomSymbol, Rates, p2);
                m_MemTickTime = Tick[i0 - 1].time_msc;
        }
}

Die obige Funktion ermöglicht es, absolut alle Handels-Ticks zu erfassen, um zu prüfen, ob es sich um Verkaufs- oder Kauf-Ticks handelt. Wenn sich diese Ticks auf BID- oder ASK-Änderungen beziehen, d. h. ohne Volumen, werden die Informationen nicht gespeichert. Dasselbe gilt für Ticks, bei denen es sich um direkte Aufträge handelt, die die Preisbewegung nicht beeinflussen, obwohl sie häufig mit der Bewegung zusammenhängen, da es Marktteilnehmer gibt, die den Preis auf einen bestimmten Wert zwingen, nur um einen direkten Auftrag auszuführen, und dann, kurz danach lassen Sie den Preis sich frei bewegen. Diese Ticks im Zusammenhang mit der BID- und ASK-Modifikation werden in einer anderen Version verwendet, die wir im nächsten Artikel sehen werden, da sie im Gesamtsystem von untergeordneter Bedeutung sind. Nachdem wir den Transaktionstyp überprüft haben, haben wir eine Reihe von Zeilen, die sehr wichtig sind und die wir verstehen sollten. Diese Zeilen im Code unten bilden eine Kerze für jeden Tick, der das Analysesystem durchlaufen hat und gespeichert werden soll.

Rates[c1].high = Tick[c0].ask;
Rates[c1].low = Tick[c0].bid;
Rates[c1].open = Tick[c0].last;
Rates[c1].close = Tick[c0].last + ((Tick[c0].volume > 200 ? 200 : Tick[c0].volume) * (Terminal.GetTypeSymbol() == C_Terminal::WDO ? 0.02 : 1.0) * iflag);
Rates[c1].time = m_LastTime;

Das Hoch und Tief der Kerze zeigt den Spread zum Zeitpunkt des Handels, d. h. der Wert, der zwischen BID und ASK bestand, ist der Schatten der erstellten Kerze, und der Eröffnungswert der Kerze ist der Preis, zu dem die Transaktion stattfand eigentlich abgeschlossen. Sehen Sie sich nun die markierte Codezeile genau an. Für einen Handels-Tick, bei dem wir Volumen haben, wird diese Linie eine kleine Anpassung an dieses Volumen vornehmen, damit die Skala nicht überläuft. Sie können die Werte nach Ihrer eigenen Analyse je nach Anlage nach eigenem Ermessen anpassen.

Jetzt das letzte Detail - die Zeit. Jede Kerze entspricht einer Minute, da es nicht möglich ist, Werte darunter darzustellen. Dann bleibt jeder von ihnen jede Minute in der entsprechenden Position. Das ist nicht Echtzeit, das ist virtuelle Zeit. Verwechseln Sie Handelszeit nicht mit grafischer Zeit: Operationen können in Millisekunden stattfinden, aber die grafischen Informationen werden jede Minute auf einer grafischen Skala dargestellt. Wir könnten jeden anderen Wert verwenden, aber dies vereinfacht die Programmierung erheblich, da er der kleinstmögliche Wert ist. Das Ergebnis dieses Systems ist unten zu sehen:

Wir sehen, dass das Lesen jetzt durchaus möglich ist und dass die Interpretation einfach ist. Zwar war das Auftragsband zum Zeitpunkt des Capturens sehr langsam, aber ich denke es reicht um die Idee zu verstehen.

Die endgültigen Informationen zu diesem System sind in der folgenden Abbildung zu sehen:

Beachten Sie, dass es vier verschiedene Konfigurationen gibt, die auf dem System zu sehen sind. Wozu werden sie benötigt? Wir werden dies im nächsten Artikel sehen, der helfen wird zu verstehen, warum es vier Times & Trade-Konfigurationen gibt. Jedenfalls haben wir bereits ein funktionierendes System, das vielleicht für eine intensive Nutzung ausreicht. Aber wenn Sie verstehen, was vor sich geht und was die Erzeugung der vier Kerzen-Muster verursacht, können Sie viel mehr aus diesem System herausholen, und wer weiß, vielleicht wird es Ihr Hauptindikator ...


Schlussfolgerung

Wir haben das System Times & Trade zur Verwendung in unserem EA entwickelt, um das Lesen von Bändern zu analysieren. Es sollte die gleiche Analysegeschwindigkeit bieten wie das alternative System in MetaTrader 5. Wir haben dies erreicht, indem wir ein Chartsystem erstellt haben, anstatt eine riesige Menge an Zahlen und Werten zu lesen und zu verstehen. Im nächsten Artikel werden wir einige fehlende Informationen im System implementieren. Wir müssen dem Code unseres Expert Advisors einige neue Elemente hinzufügen.



Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/10410

Beigefügte Dateien |
EA_-_Times_e_Trade.zip (5982.95 KB)
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 13): Times and Trade (II) Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 13): Times and Trade (II)
Heute werden wir den zweiten Teil des Systems Times & Trade (Zeiten und Handel) zur Marktanalyse aufbauen. Im vorangegangenen Artikel „Times & Trade (I)“ haben wir eine alternative Chartorganisation besprochen, die es erlauben würde, einen Indikator für die schnellstmögliche Interpretation der am Markt getätigten Geschäfte zu haben.
DoEasy. Steuerung (Teil 5): Basisobjekt von WinForms, Paneel-Steuerelement, Parameter AutoSize DoEasy. Steuerung (Teil 5): Basisobjekt von WinForms, Paneel-Steuerelement, Parameter AutoSize
In diesem Artikel werde ich das Basisobjekt aller Bibliotheks-WinForms-Objekte erstellen und mit der Implementierung der AutoSize-Eigenschaft des Paneel-Objekts für WinForms beginnen – automatische Größenanpassung zum Anpassen des internen Inhalts des Objekts.
Indikatoren mit interaktiven Steuerelementen auf dem Chart Indikatoren mit interaktiven Steuerelementen auf dem Chart
Der Artikel bietet eine neue Perspektive auf Indikatorschnittstellen. Ich werde mich auf die Bequemlichkeit konzentrieren. Nachdem ich im Laufe der Jahre Dutzende verschiedener Handelsstrategien ausprobiert und Hunderte verschiedener Indikatoren getestet habe, bin ich zu einigen Schlussfolgerungen gekommen, die ich Ihnen in diesem Artikel mitteilen möchte.
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 11): System von Kreuzaufträgen Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 11): System von Kreuzaufträgen
In diesem Artikel werden wir ein System von Kreuzaufträgen (cross order system) erstellen. Es gibt eine Art von Vermögenswerten, die den Händlern das Leben sehr schwer macht - Terminkontrakte. Aber warum machen sie einem das Leben schwer?