Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 7): Hinzufügen des Volumens zum Preis (I)

8 Juni 2022, 10:14
Daniel Jose
1
229

Einführung

Jeder, der versucht, ein gewisses Maß an Selbstvertrauen zu haben, muss diesen Indikator auf seinem Chart haben. Am häufigsten wird der Indikator von denjenigen verwendet, die das „Tape Reading“ während sie handeln vorziehen. Der Indikator kann auch von denjenigen verwendet werden, die nur auf der Grundlage der Analyse der Preisbewegungen Price Action handeln. Dies ist ein äußerst nützlicher horizontaler Volumenindikator, der verwendet werden kann, um das Handelsvolumen zu analysieren, das zu einem bestimmten Preiszeitpunkt stattgefunden hat. Es kann jedoch schwierig sein, den Indikator richtig zu lesen. Ich werde am Ende des Artikels einen Link hinzufügen, damit Sie mehr darüber erfahren können.

Hier werden wir nicht näher darauf eingehen, wie die Indikatorwerte zu interpretieren sind, da dies den Rahmen dieses Artikels sprengen würde. Der Zweck dieses Artikels besteht darin, zu zeigen, wie dieser Indikator so gestaltet und erstellt wird, dass er die Leistung der MetaTrader 5-Plattform nicht beeinträchtigt. Hier ist eine interessante Tatsache: Obwohl viele denken, dass dieser Indikator in Echtzeit aktualisiert werden sollte, ist tatsächlich eine kleine Verzögerung akzeptabel, solange sie wirklich klein ist. Aufgrund meiner eigenen Erfahrung habe ich keine großen Probleme mit einer Verzögerung bei der Aktualisierung von Informationen von etwa 1 Sekunde gesehen. Wenn es Ihnen jedoch wichtig ist, tatsächliche Echtzeit zu verwenden, müssen Sie kleine Änderungen vornehmen. Die Änderungen sollten nicht im Indikator selbst vorgenommen werden, sondern an den Stellen, an denen der Expert Advisor diesen Indikator aufruft, damit der Aufruf in Echtzeit erfolgt. Ich glaube jedoch, dass die Auswirkungen auf die Leistung minimal sein werden, sodass die Verzögerung vernachlässigt werden kann.


Schnittstelle

Die Steuerungsschnittstelle der Klasse Volume At Price ist sehr einfach, aber für eine vollständige Kontrolle ist es notwendig, die korrekten Eigenschaften des Chart sicherzustellen, auf das der Indikator angewendet wird. Die Eigenschaften sind in der folgenden Abbildung dargestellt, wobei das Hauptsteuerelement hervorgehoben ist.

Wenn das Raster nicht sichtbar ist, ist es nicht möglich, die Größe des Indikators zu ändern, wie in den folgenden Animationen gezeigt. Bitte beachten Sie, dass die Benutzeroberfläche sehr einfach und intuitiv ist: Sie hat nur zwei Bedienelemente, von denen eines die Größe anzeigt und das andere den Startpunkt für die Volumenanalyse anzeigt.

    

Im Allgemeinen ist dieser Indikator sehr effektiv und sehr interessant in der Implementierung und Konstruktion. In diesem Artikel werden wir mit der grundlegendsten Ebene arbeiten und sie dann im nächsten Artikel verbessern.

Zur Schnittstelle habe ich nichts mehr zu sagen, also machen wir weiter mit der Implementierung des Codes.


Umsetzung

Um bei der Erstellung des Indikators so wenig Arbeit wie möglich zu haben, werden wir unseren Quellcode in Teile aufteilen und auch einige Änderungen und Ergänzungen vornehmen. Beginnen wir damit, den Code in Teile zu zerlegen, da vieles von dem, was wir brauchen, bereits an anderer Stelle geschrieben wurde. Der Hauptteil davon befindet sich in der Klasse C_Wallpaper. Was werden wir machen? Werden wir einen Indikator basierend auf einer Bitmap erstellen? Ja, jedes Bild auf einem Computerbildschirm sollte als BITMAP behandelt werden, aber es sollte auf besondere Weise aufgebaut sein. Die neue Objektklasse C_Wallpaper sieht also folgendermaßen aus:

class C_WallPaper : public C_Canvas
{
        protected:
                enum eTypeImage {IMAGEM, LOGO, COR};
//+------------------------------------------------------------------+
        private :
        public  :
//+------------------------------------------------------------------+
                ~C_WallPaper()
                        {
                                Destroy();
                        }
//+------------------------------------------------------------------+
                bool Init(const string szName, const eTypeImage etype, const char cView = 100)
                        {
                                if (etype == C_WallPaper::COR) return true;
                                if (!Create(szName, 0, 0, Terminal.GetWidth(), Terminal.GetHeight())) return false;
                                if(!LoadBitmap(etype == C_WallPaper::IMAGEM ? "WallPapers\\" + szName : "WallPapers\\Logos\\" + _Symbol, cView)) return false;
                                ObjectSetInteger(Terminal.Get_ID(), szName, OBJPROP_BACK, true);

                                return true;
                        }
//+------------------------------------------------------------------+
                void Resize(void)
                        {
                                ResizeBitMap(Terminal.GetWidth(), Terminal.GetHeight());
                        }
//+------------------------------------------------------------------+
};


Sehen Sie, der Code ist viel kompakter geworden: Wir haben die Teile entfernt, die die Klassen C_Wallpaper und C_VolumeAtPrice gemeinsam haben, und alles in eine andere Klasse gesteckt, nämlich die Klasse C_C_Canvas.

Aber warum nicht die C_Canvas-Klasse von MetaTrader 5 verwenden? Die Frage ist eher persönlich als praktisch. Ich habe gerne mehr Kontrolle über alles, was ich schreibe und entwickle, aber das ist eher eine schlechte Angewohnheit für einen C-Programmierer als eine wirklich Notwendikeit. Deshalb muss ich eine Klasse zum Zeichnen von Objekten auf dem Bildschirm erstellen. Natürlich können Sie die bereits in MetaTrader 5 verfügbare Klasse verwenden. Konzentrieren wir uns nun auf die Klasse C_VolumeAtPrice, auf die sich dieser Artikel hauptsächlich konzentriert. Die Klasse hat sieben Funktionen, die in der folgenden Tabelle dargestellt sind.

Funktion Beschreibung Zugriffsart 
Init Initialisiert die Klasse mit nutzerdefinierten Werten. General
Update Aktualisiert die Daten von Volume At Price in festgelegten Intervallen. General
Resize Ändert die Größe des Bildes von Volume At Price im Chart, was die Analyse einiger Details erleichtert. General
DispatchMessage  Wird verwendet, um Nachrichten an eine Objektklasse zu senden. General
FromNowOn  Initialisiert die Systemvariablen Private
SetMatrix Erstellt und verwaltet eine Matrix mit Volumendaten. Private
Redraw Erstellt ein Volumenbild. Private

Lassen Sie uns nun mit der Implementierung des Systems fortfahren, beginnend mit der Deklaration von Variablen im folgenden Code:

#define def_SizeMaxBuff                 4096
//+------------------------------------------------------------------+
#define def_MsgLineLimit                "Starting point from Volume At Price"
//+------------------------------------------------------------------+
class C_VolumeAtPrice : private C_Canvas
{
#ifdef macroSetInteger
        ERROR ...
#endif
#define macroSetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Infos.szObjEvent, A, B)
        private :
                uint    m_WidthMax,
                        m_WidthPos;
                bool    m_bChartShift,
                        m_bUsing;
                double  m_dChartShift;
                struct st00
                {
                        ulong   nVolBuy,
                                nVolSell,
                                nVolTotal;
                        long    nVolDif;
                }m_InfoAllVaP[def_SizeMaxBuff];
                struct st01
                {
                        ulong    memTimeTick;
                        datetime StartTime,
                                 CurrentTime;
                        int      CountInfos;
                        ulong    MaxVolume;
                        color    ColorSell,
                                 ColorBuy,
                                 ColorBars;
                        int      Transparency;
                        string   szObjEvent;
                        double   FirstPrice;
                }m_Infos;


Auf den hervorgehobenen Teil in diesem Code sollten Sie achten. Dieser Teil stellt sicher, dass die Definition nicht auf eine Weise aus einer anderen Datei stammt, die mit der Definition kollidiert, die wir in dieser Datei verwenden werden. Tatsächlich zeigt der MQL5-Compiler eine Warnung an, wenn Sie versuchen, eine vorhandene Definition zu überschreiben, und in manchen Fällen ist es schwierig herauszufinden, wie man das löst. Um unser Leben ein wenig einfacher zu machen, verwenden wir daher den im obigen Code hervorgehobenen Test. Der Rest in diesem Code ist nicht besonders interessant. Das einzige, worauf Sie achten sollten, ist die Definition def_SizeMaxBuff-. Sie gibt an, wie groß unser Volumendaten-Array sein wird. Bei Bedarf können Sie diesen Wert auf einen anderen ändern, aber laut Testergebnissen ist dieser Wert für die allermeisten Fälle mehr als ausreichend. Es stellt das Schwanken der Ticks zwischen dem niedrigen Preis und dem Preis dar, und daher kann der aktuelle Wert eine Vielzahl von Fällen bewältigen.


Die Init-Funktion: wo alles beginnt

Diese Funktion initialisiert alle Variablen korrekt. Sie wird im Expert Advisor wie folgt aufgerufen:

//.... Initial data....

input color     user10   = clrForestGreen;      //Take Profit line color
input color     user11   = clrFireBrick;        //Stop line color
input bool      user12   = true;                //Day Trade?
input group "Volume At Price"
input color     user15  = clrBlack;             //Color of bars
input char      user16  = 20;                   //Transparency (from 0 to 100 )
//+------------------------------------------------------------------+
C_SubWindow             SubWin;
C_WallPaper             WallPaper;
C_VolumeAtPrice         VolumeAtPrice;
//+------------------------------------------------------------------+          
int OnInit()
{
        Terminal.Init();
        WallPaper.Init(user03, user05, user04);
        if ((user01 == "") && (user02 == "")) SubWin.Close(); else if (SubWin.Init())
        {
                SubWin.ClearTemplateChart();
                SubWin.AddThese(C_TemplateChart::SYMBOL, user02);
                SubWin.AddThese(C_TemplateChart::INDICATOR, user01);
        }
        SubWin.InitilizeChartTrade(user06, user07, user08, user09, user10, user11, user12);
        VolumeAtPrice.Init(user10, user11, user15, user16);

// ... Rest of the code

Hier gibt es nicht viele Parameter, und sie stellen hauptsächlich Informationen über die Farben dar, die der Indikator verwenden wird. Sehen wir uns als Nächstes den internen Code für diese Funktion an. Der folgende Code zeigt, wie alles initialisiert wird:

void Init(color CorBuy, color CorSell, color CorBar, char cView)
{
        m_Infos.FirstPrice = Terminal.GetRatesLastDay().open;
        FromNowOn(macroSetHours(macroGetHour(Terminal.GetRatesLastDay().time), TimeLocal()));
        m_Infos.Transparency = (int)(255 * macroTransparency(cView));
        m_Infos.ColorBars = CorBar;
        m_Infos.ColorBuy = CorBuy;
        m_Infos.ColorSell = CorSell;
        if (m_bUsing) return;
        m_Infos.szObjEvent = "Event" + (string)ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EVENT);
        CreateObjEvent();
        m_bChartShift = ChartGetInteger(Terminal.Get_ID(), CHART_SHIFT);
        m_dChartShift = ChartGetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHIFT, true);
        ChartSetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE, 0.1);
        Create("VaP" + (string)MathRand(), 0, 0, 1, 1);
        Resize();
        m_bUsing = true;
};


Wie Sie sehen können, ist alles sehr einfach. Dennoch gibt es hier einige Features, die den Code interessant machen. Einer davon ist Terminal.GetRatesLastDay().open. Obwohl dies seltsam erscheinen mag, ist es tatsächlich eine sehr häufige Situation, wenn wir den Prinzipien der objektorientierten Programmierung (OOP) folgen. Eines dieser Prinzipien besagt, dass nichts außerhalb der Klasse Zugriff auf die internen Variablen der Klasse haben sollte. Aber wie bekommt man dann die Werte von Variablen innerhalb der Klasse? Der richtige Weg ist, ein Formular zu verwenden, das nur in OOP erscheint, also sehen wir uns an, wie die Funktion GetRatesLastDay innerhalb der C_Terminal-Klasse deklariert wird. Dies ist im folgenden Code zu sehen:

inline MqlRates GetRatesLastDay(void) const { return m_Infos.Rates; }


Mal sehen, wie es tatsächlich funktioniert. Beginnen wir mit dem reservierten Wort inline. Es weist den Compiler an, dass der Code an allen Positionen platziert werden soll, an denen er erscheint. Anstatt einen Funktionsaufruf zu generieren, kopiert der Compiler tatsächlich den gesamten Code von der Funktion bis zu dem Punkt, an dem auf die Funktion verwiesen wird. Dies beschleunigt die Codeausführung aufgrund des geringeren Speicherverbrauchs. Aber in einem bestimmten Fall passiert tatsächlich, dass auf die Variable m_Infos.Rates verwiesen wird. Diese Variable hat den Typ MqlRates, d. h. wir können auf die Werte der MqlRates-Struktur zugreifen. In diesem Fall übergeben wir die Adresse der Variablenreferenz nicht. Aber in einigen Fällen übergeben wir, um den Code schneller zu machen, die Adresse der Referenz, in diesem Fall ist es möglich, den Wert einer Variablen innerhalb der Klasse zu ändern, was verboten sein sollte. Um dies zu verhindern, verwenden wir das reservierte Wort const, das garantiert, dass die Variable niemals ohne die Klasse selbst geändert wird. Während viele reservierte Wörter aus C++ sind auch in MQL5 in dokumentierter Form vorhanden, einige davon sind noch nicht dokumentiert, aber sie sind Teil von MQL5, weil es C++ sehr ähnlich ist. Am Ende des Artikels werde ich Links für diejenigen hinzufügen, die mehr über C++ erfahren und dasselbe Wissen in der MQL5-Programmierung anwenden möchten.

Jetzt haben wir im Init-Funktionscode einen interessanten Teil, den ich unten hervorgehoben habe, um zu erklären, was er tut:

m_bChartShift = ChartGetInteger(Terminal.Get_ID(), CHART_SHIFT);
m_dChartShift = ChartGetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE);
ChartSetInteger(Terminal.Get_ID(), CHART_SHIFT, true);
ChartSetDouble(Terminal.Get_ID(), CHART_SHIFT_SIZE, 0.1);


Wenn der EA gestartet wird, ändert er das Diagramm, aber es ist eine gute Praxis, das System auf seinen Anfangszustand zurückzusetzen, wenn der Nutzer es herunterfährt. Daher speichern wir die Chart-Scroll-Einstellungen und erstellen dann einen minimalen Scroll. Dies geschieht mit den hervorgehobenen Punkten, daher müssen wir das Gitter auf dem Diagramm sehen, um die Abmessungen anpassen zu können. Dies geschieht interaktiv, wie am Anfang des Artikels gezeigt. Weitere Informationen finden Sie unter unter CHART_SHIFT.


Sichern von Objekten auf dem Bildschirm

Obwohl die internen Funktionen der Klasse sehr einfach sind, gibt es einige Punkte, die besondere Aufmerksamkeit verdienen. Das erste ist ein Sicherheitssystem, das es dem Nutzer nicht erlaubt, den Punkt zu entfernen, der den Beginn der Volumenanalyse anzeigt:


Der Punkt ist sehr klein, also muss man aufpassen, dass man ihn wirklich bemerkt.

WICHTIGER HINWEIS: Wenn Sie den Analysepunkt ändern möchten, achten Sie auf den Zeitrahmen des Diagramms. Wenn Sie die Analyse beispielsweise von 9:00 Uhr auf 9:02 Uhr verschieben müssen, müssen Sie einen Zeitrahmen von 1 Minute oder 2 Minuten verwenden. Und wenn Sie beispielsweise ein Diagramm von 5 Minuten verwenden, können Sie dies nicht tun.

Als Nächstes müssen wir darauf achten, dass der Nutzer dieses Element nicht versehentlich löscht. Dies geschieht im folgenden Code:

void DispatchMessage(int iMsg, string sparam)
{
        switch (iMsg)
        {

// ... The inside of the code

                case CHARTEVENT_OBJECT_DELETE:
                        if ((sparam == m_Infos.szObjEvent) && (m_bUsing))
                        {
                                m_bUsing = false;
                                CreateObjEvent();
                                Resize();
                                m_bUsing = true;
                        }
                break;
        }                       
};


Wenn die Klasse feststellt, dass das Objekt gelöscht wurde, erstellt sie dieses Objekt sofort neu und verhindert so, dass der Nutzer ohne ein erforderliches Objekt für die Klasse zurückbleibt und somit gezwungen ist, den EA neu zu starten. Verwenden Sie das im Code gezeigte Modell immer dann, wenn Sie sicherstellen müssen, dass der Nutzer kein vertrauliches Objekt löscht. Wir müssen jedoch zusätzlichen Code hinzufügen, um sicherzustellen, dass das Ereignis vom EA bemerkt wird:

ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);

Diese einfache Zeile stellt sicher, dass MetaTrader 5 die Löschung des Objekts meldet. Siehe CHART_EVENT_OBJECT_DELETE für weitere Informationen.


Erstellen eines Volume At Price-Charts

Dies ist das Herzstück der Klasse, es hat drei Funktionen: eine öffentliche und zwei private. Beginnen wir mit der öffentlichen Funktion, sie wird unten gezeigt:

inline virtual void Update(void)
{
        MqlTick Tick[];
        int i1, p1;

        if (m_bUsing == false) return;
        if ((i1 = CopyTicksRange(Terminal.GetSymbol(), Tick, COPY_TICKS_TRADE, m_Infos.memTimeTick)) > 0)
        {
                if (m_Infos.CountInfos == 0)
                {
                        macroSetInteger(OBJPROP_TIME, m_Infos.StartTime = macroRemoveSec(Tick[0].time));
                        m_Infos.FirstPrice = Tick[0].last;
                }                                               
                for (p1 = 0; (p1 < i1) && (Tick[p1].time_msc == m_Infos.memTimeTick); p1++);
                for (int c0 = p1; c0 < i1; c0++) SetMatrix(Tick[c0]);
                if (p1 == i1) return;
                m_Infos.memTimeTick = Tick[i1 - 1].time_msc;
                m_Infos.CurrentTime = macroRemoveSec(Tick[i1 - 1].time);
                Redraw();
        };      
};

Die hervorgehobenen Zeilen sind sehr wichtig für das System. Wenn das System an die Arbeit geht, weiß es nicht genau, wo es anfangen soll. Diese Zeilen aktualisieren diese Punkte, sie informieren den Nutzer darüber, wo die Analyse begonnen hat und wie hoch der Startpreis war, damit das System eine interne Tabelle erstellen kann. Das System wartet immer auf das Eintreffen eines neuen Ticks. Sobald dies geschieht, müssen wir Daten parsen und sammeln, um sie auf dem Bildschirm anzuzeigen. Hier also die Funktion:

inline void SetMatrix(MqlTick &tick)
{
        int pos;
                                
        if ((tick.last == 0) || ((tick.flags & (TICK_FLAG_BUY | TICK_FLAG_SELL)) == (TICK_FLAG_BUY | TICK_FLAG_SELL))) return;
        pos = (int) ((tick.last - m_Infos.FirstPrice) / Terminal.GetPointPerTick()) * 2;
        pos = (pos >= 0 ? pos : (pos * -1) - 1);
        if ((tick.flags & TICK_FLAG_BUY) == TICK_FLAG_BUY) m_InfoAllVaP[pos].nVolBuy += tick.volume; else
        if ((tick.flags & TICK_FLAG_SELL) == TICK_FLAG_SELL) m_InfoAllVaP[pos].nVolSell += tick.volume;
        m_InfoAllVaP[pos].nVolDif = (long)(m_InfoAllVaP[pos].nVolBuy - m_InfoAllVaP[pos].nVolSell);
        m_InfoAllVaP[pos].nVolTotal = m_InfoAllVaP[pos].nVolBuy + m_InfoAllVaP[pos].nVolSell;
        m_Infos.MaxVolume = (m_Infos.MaxVolume > m_InfoAllVaP[pos].nVolTotal ? m_Infos.MaxVolume : m_InfoAllVaP[pos].nVolTotal);
        m_Infos.CountInfos = (m_Infos.CountInfos == 0 ? 1 : (m_Infos.CountInfos > pos ? m_Infos.CountInfos : pos));
}


Vielleicht ist diese Funktion nicht so wichtig, da sie nur Volumenwerte im Preis speichert und hält, aber die darin hervorgehobenen Zeilen sind das Herzstück des Systems. Um wirklich zu verstehen, was in diesen beiden Zeilen vor sich geht, denken wir ein wenig nach. Überlegen Sie sich Folgendes: Was ist schneller - jeden der Preise zu speichern und die Mengen in jedem von ihnen zu notieren, oder nur die Mengen zu speichern und sich zu fragen, wie hoch der Preis ist? Die zweite Option ist schneller, also speichern wir die Volumina und finden heraus, wo der Preis liegt. Aber was wird der erste Preis im System sein? Denn ja, wir brauchen einen Anfangswert, ohne den bricht alles zusammen. Wie wäre es mit dem Preis des ersten gehandelten Ticks? Ja, das ist großartig. Perfekt. Aber wir haben ein Problem: Wenn der Preis steigt, ist das großartig, alle Daten können einfach in einem Array gespeichert werden. Aber was ist, wenn er fällt? In diesem Fall haben wir negative Werte und können nicht auf ein Array mit einem negativen Index zugreifen. Wir könnten zwei Arrays anstelle von einem verwenden, aber das würde zu unnötiger Aufwand führen. Es gibt eine einfache Lösung. Schauen wir uns die folgende Tabelle an:


Wenn der Index positiv ist, müssen wir uns keine Sorgen machen, aber wenn er negativ ist, haben wir Probleme, weil wir ein bidirektionales Array verwenden, bei dem der Nullwert den Preis des ersten Ticks darstellt, die negativen Werte sind der diejenigen, die sich nach unten bewegt haben, und positive Werte sind diejenigen, die gewachsen sind. Weiter: Wenn wir zwei Richtungen haben, erhalten wir durch Multiplizieren des Index mit 2 die mittlere Spalte. Es scheint nicht zu helfen. Aber wenn wir die negativen Werte in positive umwandeln und 1 subtrahieren, erhalten wir die rechte Spalte. Wenn Sie genau hinsehen, können Sie sehen, dass die Werte in dieser rechten Spalte verschachtelt sind, was uns den perfekten Index gibt, um auf ein Array zuzugreifen, von dem wir wissen, dass es wachsen wird, aber wir wissen nicht, wie stark es wachsen wird. Und genau das tun die beiden hervorgehobenen Linien: Sie erstellen einen Index für unser Array, indem sie zwischen höheren und niedrigeren Werten als dem Startpreis wechseln. Aber obwohl dies eine sehr gute Lösung ist, nützt es nichts, wenn wir die Daten nicht auf dem Bildschirm anzeigen können, was genau das ist, was die nächste Funktion tut.

void Redraw(void)
{
        uint x, y, y1, p;
        double reason = (double) (m_Infos.MaxVolume > m_WidthMax ? (m_WidthMax / (m_Infos.MaxVolume * 1.0)) : 1.0);
        double desl = Terminal.GetPointPerTick() / 2.0;
        Erase();
        p = m_WidthMax - 8;
        for (int c0 = 0; c0 <= m_Infos.CountInfos; c0++)
        {
                if (m_InfoAllVaP[c0].nVolTotal == 0) continue;
                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, m_Infos.FirstPrice + (Terminal.GetPointPerTick() * (((c0 & 1) == 1 ? -(c0 + 1) : c0) / 2)) + desl, x, y);
                y1 = y + Terminal.GetHeightBar();
                FillRectangle(p + 2, y, p + 8, y1, macroColorRGBA(m_InfoAllVaP[c0].nVolDif > 0 ? m_Infos.ColorBuy : m_Infos.ColorSell, m_Infos.Transparency));
                FillRectangle((int)(p - (m_InfoAllVaP[c0].nVolTotal * reason)), y, p, y1, macroColorRGBA(m_Infos.ColorBars, m_Infos.Transparency));
        }
        C_Canvas::Update();
};

Diese Funktion zeichnet das Volumendiagramm, und der hervorgehobene Teil sorgt für die Umkehrung der Berechnung, die während der Volumenerfassung durchgeführt wurde. Um die Anzeige an den richtigen Punkt zu bringen, verschiebt sich der Preis ein wenig, damit die Balken richtig positioniert sind. Der Rest der Funktion ist nur das Zeichnen von Routinen. Hier sind einige Erklärungen erforderlich. Beachten Sie, dass es zwei Aufrufe von FillRectangle gibt. Warum? Der erste Aufruf zeigt an, welches Volumen größer war: Verkäufer oder Käufer, und der zweite Aufruf zeigt tatsächlich das Volumen an. Aber warum ist das nicht integriert, indem das Volumenfluss zwischen Käufern und Verkäufern aufgeteilt wird? Der Grund dafür ist, dass mit zunehmendem Volumen in einer Preisklasse die Analyse in anderen, kleineren Preisklassen beeinträchtigt wird. Es wird schwierig zu bestimmen, welches Volumen größer war, Verkauf oder Kauf. Bei dieser Darstellung verschwindet dieses Problem, wodurch das Lesen der Daten einfacher und verständlicher wird. Als Ergebnis sieht das Chart wie in der folgenden Abbildung aus:


Alle anderen Klassenfunktionen dienen als Unterstützung für die zuvor erklärten, daher ist es nicht so wichtig, sie im Detail zu behandeln.


Schlussfolgerung

Hier habe ich ein sehr einfaches Volume as Price vorgestellt, aber es ist ein äußerst effektives Werkzeug. Wenn Sie mit dem Programmieren beginnen und sich auf die objektorientierte Programmierung (OOP) konzentrieren möchten, müssen Sie diesen Code sorgfältig studieren, da er mehrere sehr gute Konzepte enthält, da der gesamte Code auf einem 100% objektorientierten Ansatz basiert .

Die Anwendung enthält den Expert Advisor bis zur aktuellen Entwicklungsstufe.


Nützliche Links

Übersetzt aus dem Portugiesischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/pt/articles/10302

Beigefügte Dateien |
EA_1.06.zip (3280.48 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
Christian

Brokerdiskussionen sind unerwünscht/verboten, aber Werbung für Broker dürfen User machen? Komische Regeln hier.

Video: Als Nächstes tragen Sie den Servernamen, Ihre Kontonummer und das Master-Passwort an. Video: Als Nächstes tragen Sie den Servernamen, Ihre Kontonummer und das Master-Passwort an.
Die Mehrheit der Studenten in meinen Kursen war der Meinung, dass MQL5 wirklich schwer zu verstehen ist. Darüber hinaus suchten sie nach einer einfachen Methode, um einige Prozesse zu automatisieren. Entdecken Sies, wie Sie sofort mit MQL5 arbeiten können, einfach durch das Lesen der in diesem Artikel enthaltenen Informationen. Selbst, wenn Sie noch nie etwas programmiert haben. Und auch für den Fall, dass Sie die vorhergehenden Illustrationen, die Sie beobachtet haben, nicht nachvollziehen können.
DoEasy. Steuerung (Teil 1): Erste Schritte DoEasy. Steuerung (Teil 1): Erste Schritte
Dieser Artikel beginnt mit einem ausführlichen Thema zum Erstellen von Steuerelementen im Windows Forms-Stil mit MQL5. Mein erstes Interessensgebiet ist das Erstellen der Panel-Klasse. Schon jetzt wird es schwierig, die Dinge ohne Kontrolle zu managen. Daher werde ich alle möglichen Steuerelemente im Windows Forms-Stil erstellen.
Lernen Sie, wie man ein Handelssystem mit dem OBV entwickelt Lernen Sie, wie man ein Handelssystem mit dem OBV entwickelt
Dies ist ein neuer Artikel, der unsere Serie für Anfänger fortsetzt, in der es darum geht, wie man ein Handelssystem basierend auf einigen der beliebten Indikatoren entwirft. Wir werden einen neuen Indikator kennenlernen, nämlich das On Balance Volume (OBV), und wir werden lernen, wie wir ihn verwenden und ein darauf basierendes Handelssystem entwerfen können.
Datenwissenschaft und maschinelles Lernen (Teil 03): Matrix-Regression Datenwissenschaft und maschinelles Lernen (Teil 03): Matrix-Regression
Diesmal werden unsere Modelle mit Hilfe von Matrizen erstellt, was uns eine gewisse Flexibilität ermöglicht, während wir gleichzeitig leistungsstarke Modelle erstellen können, die nicht nur mit fünf unabhängigen Variablen, sondern auch mit vielen Variablen umgehen können, solange wir innerhalb der Berechnungsgrenzen eines Computers bleiben.