
Erstellen von Multi-Symbol- und Multi-Perioden-Indikatoren
Inhalt
- Einführung
- Grundlegende Prinzipien
- Basisklasse des MSTF-Indikators
- Indikatorklassen nach Art
- Klasse der Indikator-Kollektion
- Tests
- Schlussfolgerung
Einführung
In Fortsetzung des Themas der Erstellung von Vorlagen für technische Indikatoren in Expert Advisors und Indikatoren, das im Artikel über die Erstellung eines Dashboards begann und dann in drei weiteren Artikeln entwickelt wurde, in denen wir Oszillatoren, Volumenindikatoren und Bill Williams sowie Trendindikatoren betrachteten. Beginnen wir heute mit dem Thema der Erstellung von Multi-Symbol- und Multi-Perioden-Indikatoren. Dazu gehören Indikatoren, die auf dem aktuellen Chart laufen, aber mit den Daten von anderen Symbolen und (oder) anderen Chart-Zeitrahmen berechnet werden. Wir werden eine Basis-Multi-Indikatorenklasse und eine Reihe von Klassen erstellen, die auf den Typen aller Standardindikatoren basieren. Wir werden auch eine Klasse für den nutzerdefinierten Indikator erstellen, mit der es möglich sein wird, einen beliebigen Indikator in einen Multi-Symbol- und Multi-Perioden-Indikator zu „verwandeln“. Für alle Indikatorklassen werden wir eine gemeinsame Kollektionsklasse erstellen. Alle im Programm erstellten Indikatoren werden in diese Kollektion aufgenommen. Außerdem wird es möglich sein, auf jeden der erstellten Indikatoren mit Hilfe der Erhebungsmethoden zuzugreifen, um die entsprechenden Indikatordaten zu erhalten. Ziel ist es, ein praktisches Werkzeug für die Erstellung von Multi-Indikatoren und die Arbeit mit deren Daten zu entwickeln.
Grundlegende Prinzipien
Um die Logik der Indikatoroperationen richtig zu verstehen, müssen wir versuchen zu begreifen, wie das Ganze funktioniert. Der Indikator besteht aus zwei Teilen: Berechnung und grafische Darstellung. Beide Teile wissen nichts voneinander. Beim Erstellen eines Indikators sucht das Terminal-Subsystem nach dem Vorhandensein eines solchen Indikators im Chart. In diesem Fall wird nach einem Indikator mit demselben Namen und denselben Parametern gesucht. Wenn ein solcher Indikator bereits auf dem Chart läuft oder programmatisch für dieses Chart erstellt wurde, dann verwendet das Terminal das Handle des vorhandenen Indikators, anstatt einen neuen zu erstellen. Der Darstellungsteil des Indikators erhält die erforderlichen Daten vom Berechnungsteil über dessen Handle. Es kann die Situation eintreten, dass mehrere Darstellungsteile gleichzeitig auf einen Berechnungsteil zugreifen.
Der Puffer des Berechnungsteils speichert die Daten des berechneten Indikators in einem Array, wobei die Daten von der Gegenwart zur Vergangenheit angeordnet sind. Die Daten bei Index 0 des Pufferarrays entsprechen den aktuellen Kartendaten:
Jede Zelle des Arrays speichert die Daten eines Balkens, der dem Zeitreihenbalken des Symbols/der Periode entspricht, für den der Indikator berechnet wird. Um also Daten aus dem Puffer des Berechnungsteils des Indikators zu erhalten und sie auf dem Chart eines anderen Symbols/Zeitrahmens anzuzeigen, müssen Sie die Balkennummer auf dem Chart berechnen, die der Zeit des Balkens im Pufferfeld des Berechnungsteils entspricht. Die erhaltenen Daten sollten in den Puffer des Darstellungsteils geschrieben werden, sodass alle Balken des aktuellen Charts, die mit der Öffnung des Balkens im Puffer des Berechnungsteils übereinstimmen, zu den entsprechenden Zellen des Puffers für die Darstellung hinzugefügt werden.
So entspricht beispielsweise ein Balken in einem Fünf-Minuten-Chart fünf Balken eines Ein-Minuten-Charts. Alle diese fünf Balken des Ein-Minuten-Charts müssen mit dem Wert des Fünf-Minuten-Balkens gefüllt werden, der ihnen zeitlich entspricht. Ein ähnlicher Algorithmus wird verwendet, um Daten aus niedrigeren Zeiträumen in einem Chart mit höherem Zeitrahmen darzustellen. In diesem Fall werden alle Balken aus den Zellen des Berechnungsteilpuffers, die dem Zeitpunkt des Balkens auf dem übergeordneten Zeitrahmen (TF, Timeframe) entsprechen, auf einem Balken des Zeichnungspuffers geplottet.
Natürlich kann es sein, dass die Messwerte nicht genau stimmen, da dieser Balken schließlich nur die Daten des letzten niedrigeren Zeitrahmen-Balkens darstellt, der mit dem Zeitpunkt des entsprechenden höheren Zeitrahmen-Balkens übereinstimmt. Hier hängt alles von der Empfangsrichtung der Daten aus dem Berechnungsteilpuffer des unteren Zeitrahmens ab, sodass die zuletzt empfangenen Daten auf dem Balken des Charts mit dem höheren Zeitrahmen gezeichnet werden.
Die Funktion CopyBuffer() holt sich die Daten aus dem Puffer des berechneten Indikators:
Die Funktion empfängt die Daten des angegebenen Indikatorpuffers in der angegebenen Menge in das Array ‚buffer‘.
Die Elemente der kopierten Daten (Indikatorpuffer mit dem Index buffer_num) werden von der Startposition aus gezählt, von der Gegenwart zur Vergangenheit, d.h. die Startposition gleich 0 bedeutet den aktuellen Balken (den Indikatorwert für den aktuellen Balken).
Wenn die Menge der zu kopierenden Daten nicht im Voraus bekannt ist, ist es ratsam, ein dynamisches Array als Ziel-Array buffer[] zu verwenden, da CopyBuffer() versucht, die Größe des empfangenden Arrays auf die Menge der kopierten Daten zu verteilen. Wenn das empfangende Array buffer[] ein Indikatorpuffer ist (ein Array, das zuvor von der Funktion SetIndexBufer() zur Speicherung von Indikatorwerten zugewiesen wurde), ist ein teilweises Kopieren erlaubt.
Wenn Sie die Indikatorwerte teilweise in ein anderes Array (nicht den Indikatorpuffer) kopieren müssen, sollten Sie ein Zwischenarray verwenden, in das die gewünschte Menge kopiert wird. Kopieren Sie aus diesem Zwischenfeld die erforderliche Anzahl von Werten Glied für Glied an die erforderlichen Stellen des empfangenden Feldes.
Wenn Sie eine bestimmte Datenmenge kopieren müssen, empfiehlt es sich, einen statisch zugewiesenen Puffer zu verwenden, um eine unnötige Neuzuweisung von Speicher zu vermeiden.
Die Eigenschaft des empfangenden Arrays, d.h. as_series=true oder as_series=false, wird ignoriert: Beim Kopieren wird das älteste Element an den Anfang des für das Array reservierten physischen Speichers kopiert. Es gibt drei Funktionsmöglichkeiten.
Zugriff über die Ausgangsposition und die Anzahl der benötigten Elemente
int CopyBuffer( int indicator_handle, // indicator handle int buffer_num, // indicator buffer index int start_pos, // starting point int count, // amount to copy double buffer[] // array the data to be copied to );
Zugang über das Anfangsdatum und der Anzahl der erforderlichen Elemente
int CopyBuffer( int indicator_handle, // indicator handle int buffer_num, // indicator buffer index datetime start_time, // starting date int count, // amount to copy double buffer[] // array the data to be copied to );
Zugang zu den Anfangs- und Enddaten des gewünschten Zeitraums
int CopyBuffer( int indicator_handle, // indicator handle int buffer_num, // indicator buffer index datetime start_time, // starting date datetime stop_time, // end date double buffer[] // array the data to be copied to );
Parameter
indikator_handle
[in] Indikatorhandle, das durch die entsprechende Indikatorfunktion erhalten wird.
buffer_num
[in] Nummer des Indikatorpuffers.
start_pos
[in] Index des ersten kopierten Elements.
count
[in] Anzahl der kopierten Elemente.
start_time
[in] Balkenzeit für das erste Element.
stop_time
[in] Balkenzeit für das letzte Element.
buffer[]
[out] Array vom Typ Double.
Rückgabewert
Die Anzahl der kopierten Array-Elemente oder -1 im Falle eines Fehlers.
Hinweis
Wenn Daten von einem Indikator angefordert werden, gibt die Funktion sofort -1 zurück, wenn die angeforderten Zeitreihen noch nicht erstellt sind oder vom Server heruntergeladen werden sollen. In diesem Fall wird das Herunterladen oder die Erstellung der erforderlichen Daten eingeleitet.
Wenn Daten von einem EA oder einem Skript angefordert werden, wird der Download vom Server eingeleitet, wenn das Terminal nicht über die entsprechenden Daten vor Ort verfügt. Optional beginnt die Konstruktion der erforderlichen Zeitreihen, wenn die Daten aus dem lokalen Verlauf konstruiert werden können, aber noch nicht fertig sind. Die Funktion gibt den Betrag zurück, der zum Zeitpunkt des Ablaufs der Zeitüberschreitung zur Verfügung steht.
Wir werden die erste Version der Funktion verwenden, d. h. den Zugriff auf der Grundlage der Startposition (y der Schleifenindex) und der Anzahl der erforderlichen Elemente.
Die Objektstruktur wird wie folgt aussehen:
- Die Basisklasse eines Multi-Symbol- und Multi-Perioden-Indikators, die die wichtigsten Funktionen enthält, die allen Indikatoren gemeinsam sind;
- Von dem Basisobjekt abgeleitete Klassen, die jeden Indikator durch seinen Typ beschreiben;
- Eine Kollektion von Indikatoren – Sie können diese Klasse verwenden, um beliebige Indikatoren zu erstellen und sie der Kollektion hinzuzufügen. Die Klasse stellt dem Nutzer alle Werkzeuge zur Verfügung, um Indikatoren zu erstellen und Daten von ihnen zu erhalten.
Wenn Sie mit Daten aus einem nicht aktuellen Chart arbeiten, sollten Sie mindestens alle zwei Minuten auf diese Zeitreihe zugreifen, um die Freigabe der Zeitreihe zu vermeiden. In diesem Fall wird die Zeitreihe „vorgehalten“, was den Zugriff darauf beschleunigt (man muss nicht jedes Mal auf die Synchronisierung der Daten warten). Im Klassenkonstruktor führen wir den ersten Aufruf der Zeitreihe aus, auf der der Indikator aufgebaut ist. Dadurch können wir mit dem Herunterladen der Zeitreihe beginnen (falls kein lokaler Zugriff darauf besteht). Dann werden wir alle 90 Sekunden in der Basisklasse Timer auf die Zeitreihe zugreifen, um sie zu halten.
Ressourcenschonende Berechnungen
Um den Indikator zu berechnen und anzuzeigen, benötigen wir eine ressourceneffiziente Berechnungsmethode. Der Indikator wird beim ersten Start für alle historischen Daten berechnet und wird dann für einen oder zwei Balken für alle nachfolgenden Ticks berechnet.
OnCalculate() verfügt über vordefinierte, konstante Variablen, die die Größe der Eingabedaten (einer Zeitreihe oder eines Arrays) und die Datenmenge, die beim vorherigen Aufruf von OnCalculate() berechnet wurde, speichern:
Berechnung auf Basis eines Datenarrays
int OnCalculate( const int rates_total, // price[] array size const int prev_calculated, // number of processed bars at the previous call const int begin, // index number in the price[] array meaningful data starts from const double& price[] // array of values for calculation );
Berechnung auf Basis der aktuellen Zeitreihendaten des Zeitrahmens
int OnCalculate( const int rates_total, // size of input timeseries const int prev_calculated, // number of processed bars at the previous call const datetime& time{}, // Time array const double& open[], // Open array const double& high[], // High array const double& low[], // Low array const double& close[], // Close array const long& tick_volume[], // Tick Volume array const long& volume[], // Real Volume array const int& spread[] // Spread array );
Das Vorhandensein solcher Daten ermöglicht eine schnelle und effiziente Berechnung des Indikators. Betrachten wir zum Beispiel die Berechnung für „limit“:
//--- Number of bars for calculation int limit=rates_total-prev_calculated; //--- If limit > 1, then this is the first calculation or change in the history if(limit>1) { //--- specify all the available history for calculation limit=rates_total-1; //--- If the indicator has any buffers, initialize them here with empty values set for these buffers }
Wenn der Indikator zum ersten Mal gestartet wird, haben wir die Größe der Zeitreihe (rates_total) und die Menge der berechneten Daten beim letzten Aufruf (prev_calculated). Der Wert der zuvor berechneten Balken ist beim ersten Start Null, da der Indikator noch nicht berechnet wurde. Der Wert von „limit“ ist also größer als 1 (er entspricht der Anzahl der verfügbaren Balken minus Null). Mit diesem Wert geben wir „limit“ gleich rates_total-1 an - die gesamte verfügbare Historie für die Berechnung. In diesem Fall müssen wir zusätzlich zuerst alle zuvor gezeichneten Daten aus den Indikatorpuffern entfernen, zum Beispiel:
ArrayInitialize(Buffer,EMPTY_VALUE);
Danach wird die gesamte Historie in der Hauptschleife berechnet, die von „limit“ bis einschließlich Null durchlaufen wird:
for(int i=limit;i>=0;i--) { // Calculating indicator at each bar of the loop (i) }
Bitte beachten Sie, dass bei dieser Berechnungsmethode alle in den Berechnungen verwendeten Arrays und der Indikatorpuffer selbst das Flag der Indizierung als Zeitreihe haben müssen:
ArraySetAsSeries(array,true);
Wenn der berechnete „limit“ gleich 1 ist, bedeutet dies die Eröffnung eines neuen Balkens auf dem Chart: Der Indikator berechnet den ersten und den Null-Balken der Zeitreihe in einer Schleife von 1 bis einschließlich 0.
Wenn der berechnete „limit“ gleich 0 ist, bedeutet dies, dass der Indikator mit dem aktuellen Tick arbeitet: Er berechnet nur den Null-Balken der Zeitreihe in einer Schleife von 0 bis einschließlich 0.
Wenn Sie vom Null-Balken aus tief in die historischen Daten hineinrechnen müssen (um die Zeitreihen-Arrays und Puffer nicht zu erweitern), wird die Schleife umgekehrt:
int i=fmax(0, prev_calculated-1); for(;i<rates_total;i++) { // Calculating indicator at each bar of the loop (i) }
Es ist recht einfach, ressourcensparende Berechnungen auf dem aktuellen Chart durchzuführen. Was aber, wenn Sie Daten benötigen, die nicht aus dem aktuellen Chart stammen? Wann kopieren Sie das gesamte Datenfeld aus dem Berechnungsteil und wann nur die letzten ein oder zwei Balken?
Hier werden wir die Funktionen Bars() und BarsCalculated() verwenden. Dies sind Analoga zu den vordefinierten konstanten Indikatorvariablen rates_total und prev_calculated. Sie geben die Anzahl der Balken für das angegebene Symbol/die angegebene Periode und die vom Indikator berechnete Datenmenge zurück. Da der Indikator für das Symbol/die Periode konstruiert wird, das/die bei seiner Erstellung angegeben wurde, bezieht sich die Menge der berechneten Daten auch auf dieses Symbol/diese Periode. Wir holen uns den Indikator über sein Handle.
Da wir berechnen können, wie viele Balken wir für ein beliebiges Symbol/eine beliebige Periode kopieren müssen (um nicht bei jedem Tick das gesamte Array zu kopieren), erstellen wir in der Basisklasse des Indikators genau die gleiche Konstruktion wie für das aktuelle Symbol/die aktuelle Periode:
limit=rates_total-prev_calculated;
Die Variablen „limit“, „rates_total“ und „prev_calculated“ sind jedoch private Mitglieder der Klasse und erhalten Werte von den Funktionen Bars() und BarsCalculated(). „limit“ wird bei jedem Tick berechnet, und wenn er Null ist, werden nur die Daten der letzten beiden Datenbalken (aktueller und vorheriger Balken) kopiert. Wenn „limit“ gleich 1 ist, bedeutet dies die Eröffnung eines neuen Balkens auf dem Indikatorsymbol/der Periode, und Sie müssen das Array um 1 erhöhen und dann die Daten aus dem Indikatorpuffer kopieren - ebenfalls zwei. Wenn „limit“ größer als eins ist, wird das gesamte Array aus dem Berechnungsteilpuffer des Indikators in den empfangenden Array-Puffer der Klasse kopiert, da davon ausgegangen wird, dass es sich entweder um den ersten Start oder eine Änderung in der Geschichte handelt.
Diese Logik gilt für den Handelstag, wenn die Ticks eintreffen.
Ein Feiertag erfordert einen anderen Ansatz. Dies ist ein Einzelfall. Hier gibt es keine Ticks, die Funktion Bars() gibt sehr oft Null zurück, und die Anzahl der berechneten Indikatorbalken wird nicht aufgezeichnet, d.h. ist ebenfalls Null. Liegt ein Fehler in den für die Berechnung verwendeten Quelldaten vor, sollte der Indikator Null ergeben. Das bedeutet, dass er bis zum nächsten Tick wartet und dann erneut versucht zu rechnen. Aber an einem Feiertag gibt es keinen Tick außer dem ersten Start.
Beim ersten Start wird der Indikator zunächst die Pufferfelder leeren und dann die Berechnung durchführen. Die Berechnung kann jedoch fehlschlagen, weil nicht genügend Daten für die Berechnung zur Verfügung stehen oder weil prev_calculated einfach einen Nullwert zurückgibt. Der Indikator beendet OnCalculate() und gibt wieder Null zurück. Wenn Sie also das Chart über ein Rechtsklick-Menü aktualisieren, was als Tick-Emulation betrachtet wird, sieht der Indikator erneut, dass er mit einem Fehler berechnet wurde, und initialisiert die Puffer erneut, da er davon ausgeht, dass dies der erste Start ist. Dieses Verhalten setzt sich fort, und die soeben auf dem Chart gerenderten Zeichnungspufferdaten werden ständig gelöscht... Es gibt jedoch eine Lösung.
Wenn es beim ersten Start nicht möglich war, den Indikator sofort zu berechnen, dann können Sie 20 - 30 Sekunden auf den nächsten Tick warten und diesen programmatisch emulieren. Dies kann mit der Funktion ChartSetSymbolPeriod() erfolgen. Wenn Sie diese Funktion aufrufen und das aktuelle Chartsymbol bzw. die aktuelle Periode angeben, werden die Indikatoren, die auf dem Chart laufen, neu berechnet. So können Sie den Indikator auf dem Chart berechnen, auch wenn es keine Ticks gibt.
Eine Wartezeit von zwanzig Sekunden reicht aus, um die erforderliche Historie für das Indikatorsymbol/die Periode zu laden und zu berechnen. Aber wir brauchen ein Flag, das anzeigt, dass der Indikator bereits berechnet wurde (da prev_calculated Null zurückgibt), und wenn wir das Flag für den Berechnungserfolg nicht setzen, wird der Indikator seine Puffer wieder leeren. Wir können also erkennen, dass der Indikator erfolgreich berechnet wurde, indem wir einfach sehen, dass die Anzahl der berechneten Balken für das Indikatorobjekt gleich der Anzahl der Balken seines Symbols/Periode ist. Wenn Bars() Null zurückgibt, können wir auf andere Weise die verfügbare Anzahl von Balken des gewünschten Symbols/der gewünschten Periode herausfinden (vergessen Sie nicht, dass wir über Multi-Symbol- und Multi-Perioden-Indikatoren sprechen, die in einem anderen Indikator oder Expert Advisor berechnet werden, der auf dem aktuellen Chart läuft). Mit der Funktion SeriesInfoInteger() können wir das Start- und Enddatum der verfügbaren Historie des Symbols/des Zeitraums abrufen:
SeriesInfoInteger(symbol,period,SERIES_FIRSTDATE); // The very first date for a period and symbol at the moment SeriesInfoInteger(symbol,period,SERIES_LASTBAR_DATE); // Time of opening the last bar for the period and symbol
Anhand dieser beiden Daten und der Periode des Zeitreihencharts lässt sich die Anzahl der verfügbaren Balken leicht berechnen, selbst wenn Bars(Symbol,Periode) oder SeriesInfoInteger(Symbol,Periode,SERIES_BARS_COUNT) den Wert Null ergeben.
Wenn alle Daten eingegangen sind und der Indikator korrekt berechnet wurde, wird das Flag für die erfolgreiche Berechnung gesetzt. Dieses Flag wird geprüft, wenn limit > 1 ist (in diesem Fall müssen die Indikatorpuffer-Arrays mit einem „leeren“ Wert initialisiert werden). Beim ersten Start wird das Erfolgsflag zurückgesetzt, die Arrays werden initialisiert und es wird versucht, den Indikator für ein nicht aktuelles Chartsymbol/eine nicht aktuelle Periode zu berechnen. Wenn die Berechnung fehlgeschlagen ist, warten Sie 20 Sekunden (gemäß einem Timer) auf den nächsten Tick.
Wenn es ein Wochenende ist, dann gibt es keinen Tick, und nach 20 Sekunden wird ein Befehl gesendet, um das aktuelle Symbol und die Periode für das Chart zu setzen, um einen Tick zu emulieren. Beim Neustart des Indikators im Timer (nach 20 Sekunden) sollten die Daten bereits geladen sein und der Indikator sollte fehlerfrei berechnet werden. In diesem Fall wird das Erfolgs-Flag der Berechnung gesetzt. Bei der nächsten Aktivierung des Zeitgebers wird dieses Flag überprüft, und wenn es gesetzt ist, wird der Tick nicht emuliert. Es gibt drei solcher Versuche, Indikatorpuffer darzustellen. Nach drei Versuchen wird das Erfolgs-Flag zwangsweise gesetzt, und die Versuche, den Tick zu emulieren, werden abgebrochen. Wenn der Indikator drei emulierte Ticks lang nicht berechnen konnte, ist nur eine manuelle Aktion möglich: entweder über das Rechtsklickmenü aktualisieren oder den Zeitrahmen des Charts hin- und herschalten, um den gesamten Tick-Emulationsprozess mit dem Laden der Daten erneut zu durchlaufen.
Das ist die Theorie. Gehen wir zur Praxis über: Erstellen von Klassen von Multi-Indikatoren.
Basisklasse des MSTF-Indikators
Im Terminalordner \MQL5\Include\IndMSTF\ erstellen wir eine neue Datei IndMSTF.mqh für die Klasse CIndMSTF (MS=Multi-Symbol, TF=Timeframe bzw. Zeitrahmen), Die Klasse muss von dem Basisobjekt CObject der Standardbibliothek abgeleitet werden. Die Basisobjektdatei muss von der neu erstellten Klassendatei eingebunden werden:
//+------------------------------------------------------------------+ //| IndMSTF.mqh | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" //--- includes #include <Object.mqh> //+------------------------------------------------------------------+ //| Base class of the multi-symbol multi-period indicator | //+------------------------------------------------------------------+ class CIndMSTF : public CObject { private: protected: public: //--- Constructor/destructor CIndMSTF(); ~CIndMSTF(); };
Bevor wir den verschiedenen Abschnitten der Klasse Methoden hinzufügen, wollen wir einige Makro-Ersetzungen, Enumerationen und die Struktur des Indikatorpuffers hinzufügen.
Wir brauchen einen zweiten Timer, der zwei Zeiträume erfasst:
- 90 Sekunden, danach werden wir uns den Zeitreihen der Indikatoren zuwenden, die nicht für das aktuelle Symbol/den aktuellen Zeitraum berechnet werden;
- 20 Sekunden, wonach wir einen Tick emulieren, um den Indikator am Wochenende darzustellen.
//--- includes #include <Object.mqh> //--- defines #define TIMER_COUNT_1 (90) // Timer 1 size. Must not be more than two minutes (120) #define TIMER_COUNT_2 (20) // Timer 2 size. Too small values quickly trigger tick emulation, which is not desirable in an active market
Verschiedene Standardindikatoren im Kundenterminal gehören zu unterschiedlichen Kategorien. Damit wir die erstellten Indikatoren nach Kategorien sortieren oder Listen von Indikatoren erstellen können, die zu einer beliebigen Kategorie gehören, schreiben wir eine Enumeration der verschiedenen Indikatorkategorien:
//--- defines #define TIMER_COUNT_1 (90) // Timer 1 size. Must not be more than two minutes (120) #define TIMER_COUNT_2 (20) // Timer 2 size. Too small values quickly trigger tick emulation, which is not desirable in an active market //--- enums enum ENUM_IND_CATEGORY // Indicator categories { IND_CATEGORY_NONE, // Not set IND_CATEGORY_TREND, // Trend IND_CATEGORY_OSCILLATOR, // Oscillators IND_CATEGORY_VOLUME, // Volume IND_CATEGORY_WILLIAMS, // Bill Williams IND_CATEGORY_CUSTOM, // Custom };
Über Sortieren, Suchen und Filtern
Jedes Indikatorobjekt verfügt über Eigenschaften, mit denen sich der gewünschte Indikator finden lässt. Um zu verstehen, dass Indikatoren identisch sind, müssen wir ihre Schlüsseleigenschaften vergleichen: Symbol, Chartperiode und Werte aller Eingabeparameter. Wenn mindestens ein Wert der zu vergleichenden Eigenschaften unterschiedlich ist, sind die Indikatoren nicht identisch. Wenn die Indikatoren gleich sind, wird kein neuer Indikator erstellt, sondern ein Zeiger auf den zuvor erstellten Indikator mit denselben Parametern zurückgegeben. Dies betrifft die Kollektion von Indikatoren. Was die Eigenschaften betrifft, müssen wir eine Enumeration erstellen, die Konstanten einiger Eigenschaften enthält, die auf einen erfolgreich erstellten Indikator gesetzt werden können und dann verwendet werden können, um diesen Indikator zu finden:
enum ENUM_COMPARE_MODE // Comparison mode { // By default, the comparison mode is set to zero, which compares all properties COMPARE_MODE_HANDLE=1, // Compare by handle COMPARE_MODE_SYMBOL, // Compare by symbol COMPARE_MODE_TIMEFRAME, // Compare by chart period COMPARE_MODE_ID, // Compare by ID COMPARE_MODE_DESCRIPTION, // Compare by custom description COMPARE_MODE_CATEGORY, // Compare by category };
Jeder erfolgreich erstellte Indikator hat einen Handle, über den auf ihn zugegriffen werden kann. Dies ist eine eindeutige Nummer, die dem Berechnungsteil des Indikators zugeordnet ist. Die Handle-Werte der erstellten Indikatoren beginnen bei 10 und erhöhen sich bei jedem weiteren Wert um 1.
Die übrigen Eigenschaften sind nicht eindeutig und können in verschiedenen Indikatoren enthalten sein. Die Suche anhand dieser Werte wird hier nur der Einfachheit halber angeboten. Sie können beispielsweise die eindeutigen Eigenschaften für einen Indikator festlegen und dann mit diesen Eigenschaften auf ihn verweisen.
Die Eigenschaften der Indikatorlinien wurden bereits in früheren Artikeln über Indikatoren erörtert. Auch hier werden wir diese Enumeration verwenden:
enum ENUM_LINE_STATE // Indicator line state { LINE_STATE_NONE, // Undefined LINE_STATE_UP, // Upward LINE_STATE_DOWN, // Downward LINE_STATE_TURN_UP, // Upward reversal LINE_STATE_TURN_DOWN, // Downward reversal LINE_STATE_STOP_UP, // Upward stop LINE_STATE_STOP_DOWN, // Downward stop LINE_STATE_ABOVE, // Above value LINE_STATE_BELOW, // Below value LINE_STATE_CROSS_UP, // Upward value crossing LINE_STATE_CROSS_DOWN, // Downward value crossing LINE_STATE_TOUCH_BELOW, // Touching value from below LINE_STATE_TOUCH_ABOVE, // Touching value from above LINE_STATE_EQUALS, // Equal to value };
Weitere Einzelheiten entnehmen Sie bitte dem Artikel über Oszillator-Indikatoren im Abschnitt ATR-Indikatorparameter.
Jeder Indikator signalisiert das Ergebnis seiner Berechnung durch einen Fehlercode:
enum ENUM_ERR_TYPE // Indicator calculation error type { ERR_TYPE_NO_ERROR, // No error ERR_TYPE_NO_CYNC, // Data not synchronized ERR_TYPE_NO_DATA, // Data not loaded ERR_TYPE_NO_CALC, // Calculation not completed };
Mit Hilfe dieses Codes kann von außen ermittelt werden, welche Maßnahmen zur Behandlung des Fehlers erforderlich sind.
Indikatorpuffer
Hier müssen wir entscheiden, wo die Puffer hingehören.
- Puffer des Berechnungsteils. Wenn wir einen Indikator erstellen, wird er im Speicher angelegt. Dies ist der Berechnungsteil des Indikators. Es hat seine eigenen Puffer, die vom Terminal-Subsystem verwaltet werden. Sie können auf den Berechnungsteil über den Handle zugreifen, der nach erfolgreicher Erstellung des Indikators zurückgegeben wird. Der Puffer eines erfolgreich erstellten und berechneten Indikators enthält immer die Daten, die der Zeitreihe entsprechen, für die der Indikator berechnet wurde. Die Daten in diesem Puffer sind so angeordnet, dass der Nullindex dem aktuellen Balken der Zeitreihe entspricht, aus der der Indikator berechnet wird.
Um Daten aus dem Puffer des Berechnungsteils des Indikators zu kopieren, verwenden wir die Funktion CopyBuffer(). - Puffer des Indikatorobjekts. Jedes der erstellten Objekte der Klasse Multi-Indikator hat seine eigenen Puffer-Arrays entsprechend der Anzahl der Puffer dieses Indikators. Die Daten aus dem Berechnungsteilpuffer werden in diesen Arrays abgelegt. Die Puffer des Indikatorobjekts werden innerhalb des Klassenobjekts verwaltet, in dem sie initialisiert, entsprechend der Größe der Zeitreihe, für die der Indikator erstellt wird, vergrößert und bei jedem neuen Tick aktualisiert werden. Beim Kopieren von Daten aus dem Berechnungsteilpuffer in das Indikatorobjekt-Array mittels CopyBufer() werden die Daten so angeordnet, dass sich der aktuelle Balken am Ende des Arrays befindet (ArraySize()-1).
- Puffer des Teils für die Indikatordarstellung. Jedes Indikatorobjekt kann sowohl in einem Expert Advisor als auch in einem nutzerdefinierten Indikator erstellt werden. Bei der Erstellung von Multi-Indikatoren in Expert Advisors rufen wir zur Berechnung der Indikatoren die Methode auf, die den Indikator berechnet, und um die berechneten Daten zu erhalten, greifen wir auf den gewünschten Pufferindex des Indikatorobjekts zu. Im Falle eines nutzerdefinierten Indikators müssen wir auch die Daten der Multi-Indikatoren im Chart darstellen. Deshalb haben wir hier auch einen Puffer für die Darstellung. Dieser Puffer wird für die Darstellung in einem nutzerdefinierten Indikator zugewiesen. Es zeigt Daten an, die in den Puffern der Indikatorobjekte gespeichert sind. Um Linien in einem Chart anzuzeigen, genügt es, die Methode der Indikatorkollektionsklasse des nutzerdefinierten Indikators aufzurufen, die die Indikatoren berechnet, und dann, wenn die Berechnung erfolgreich war, die Methode aufzurufen, die die Pufferdaten des Indikatorobjekts in den Zeichnungspuffer des nutzerdefinierten Indikators einfügt.
Im Indikatorobjekt wird ein Puffer durch eine Struktur dargestellt, die sowohl das dynamische Array selbst als auch die Steuerelemente für dieses Array enthält:
//--- struct struct SBuffer // Structure of the indicator buffer { double array[]; // Indicator buffer array double init_value; // Initializing value int shift; // Horizontal shift of the buffer string descript; // Buffer description //--- (1) Sets, (2) returns the initializing value, void SetInitValue(const double value) { init_value=value; } double InitValue(void) { return init_value; } //--- (1) Sets, (2) returns the buffer offset void SetShift(const int value) { shift=value; } int Shift(void) { return shift; } //--- (1) Resizes the buffer array, (2) returns the size of the buffer array, //--- (3) initializes the array with the set "empty" value bool BuffResize(const int new_size) { return(ArrayResize(array,new_size)==new_size);} uint BufferSize(void) { return array.Size(); } int InitBuffer(void) { return ArrayInitialize(array,init_value); } };
Einige Werte, die extern gesetzt werden, z. B. bei der Erstellung eines Indikators, müssen irgendwo gespeichert werden, damit man später weiß, was diese Werte waren. Der einfachste Weg ist, sie direkt in die Struktur zu schreiben. Das tun wir hier: Der vom aufrufenden Programm eingestellte „leere“ Pufferwert wird in der Pufferstruktur in der Variablen init_value gespeichert. Der Versatz (shift) der Indikatorlinie, der bei der Erstellung des Berechnungsteils des Indikators festgelegt wird, kann hier ebenfalls gespeichert werden, sodass man sie später innerhalb des Klassenobjekts kennt; sie wird in der Variablen „shift“ gespeichert. Auch die Eigenschaften des Puffers werden hier gespeichert. Diese Eigenschaften werden automatisch beim Anlegen des Berechnungsteils des Indikators gesetzt und entsprechen dem Namen des Puffers für denselben Standardindikator. Diese Eigenschaften können später geändert werden.
Funktionen, die die Eigenschaften des Indikatorlinienzustands und die Fehler bei der Berechnung des Indikators zurückgeben, sind nur notwendig, um die Zustände der Indikatorlinie und die Fehler bei ihrer Initialisierung und Berechnung zu protokollieren oder auf dem Informationspanel anzuzeigen:
//+------------------------------------------------------------------+ //| Return the indicator line state description | //+------------------------------------------------------------------+ string BufferLineStateDescription(const ENUM_LINE_STATE state) { switch(state) { case LINE_STATE_NONE : return "None"; case LINE_STATE_UP : return "Up"; case LINE_STATE_STOP_UP : return "Stop Up"; case LINE_STATE_TURN_UP : return "Turn Up"; case LINE_STATE_DOWN : return "Down"; case LINE_STATE_STOP_DOWN : return "Stop Down"; case LINE_STATE_TURN_DOWN : return "Turn Down"; case LINE_STATE_ABOVE : return "Above level"; case LINE_STATE_BELOW : return "Below level"; case LINE_STATE_CROSS_UP : return "Crossing Up"; case LINE_STATE_CROSS_DOWN : return "Crossing Down"; case LINE_STATE_TOUCH_BELOW: return "Touch from Below"; case LINE_STATE_TOUCH_ABOVE: return "Touch from Above"; case LINE_STATE_EQUALS : return "Equals"; default : return "Unknown"; } } //+------------------------------------------------------------------+ //| Return error description in indicator calculation | //+------------------------------------------------------------------+ string TypeErrorcDescription(ENUM_ERR_TYPE error_type) { switch(error_type) { case ERR_TYPE_NO_CYNC : return "Data is not synchronized"; case ERR_TYPE_NO_DATA : return "Data not loaded"; case ERR_TYPE_NO_CALC : return "Calculation not completed"; default : return "No error"; } }
Die erforderlichen Vorbereitungsarbeiten wurden bereits durchgeführt. Kommen wir nun zur Objektklasse eines Multi-Symbol-Multi-Perioden-Indikators.
Schreiben wir in den Körper der Klasse alle privaten, geschützten und öffentlichen Variablen und Methoden, die für das Funktionieren der Klasse notwendig sind, und betrachten wir dann ihren Zweck und ihre Implementierung:
//+------------------------------------------------------------------+ //| Base class of the multi-symbol multi-period indicator | //+------------------------------------------------------------------+ class CIndMSTF : public CObject { private: ENUM_PROGRAM_TYPE m_program; // Program type ENUM_INDICATOR m_type; // Indicator type ENUM_TIMEFRAMES m_timeframe; // Chart timeframe string m_symbol; // Chart symbol int m_handle; // Indicator handle int m_id; // Identifier bool m_success; // Successful calculation flag ENUM_ERR_TYPE m_type_err; // Calculation error type string m_description; // Custom description of the indicator string m_name; // Indicator name string m_parameters; // Description of indicator parameters protected: ENUM_IND_CATEGORY m_category; // Indicator category MqlParam m_param[]; // Array of indicator parameters string m_title; // Title (indicator name + description of parameters) SBuffer m_buffers[]; // Indicator buffers int m_digits; // Digits in indicator values int m_limit; // Number of bars required to calculate the indicator on the current tick int m_rates_total; // Number of available bars for indicator calculation int m_prev_calculated; // Number of calculated bars on the previous indicator call //--- (1) Sets indicator name, (2) description of parameters void SetName(const string name) { this.m_name=name; } void SetParameters(const string str) { this.m_parameters=str; } //--- Resizes the (1) specified, (2) all indicator buffers bool BufferResize(const uint buffer_num,const int new_buff_size); bool BuffersResize(const int new_buff_size); //--- Initializes the (1) specified, (2) all indicator buffers bool BufferInitialize(const uint buffer_num,const int new_buff_size); bool BuffersInitialize(const int new_buff_size); //--- Returns the flag indicating equality of the structure of one parameter of two objects bool IsEqualParameters(const MqlParam &this_param,const MqlParam &compared_param) const { if(this_param.type==compared_param.type && this_param.integer_value==compared_param.integer_value && this_param.string_value==compared_param.string_value && ::NormalizeDouble(this_param.double_value-compared_param.double_value,8)==0 ) return true; return false; } //--- Return the result of comparison on one parameter of two objects int CompareParams(const MqlParam &this_param,const MqlParam &compared_param) { if(this.IsEqualParameters(this_param,compared_param)) return 0; else if(this_param.type>compared_param.type || this_param.integer_value>compared_param.integer_value || this_param.string_value>compared_param.string_value || this_param.double_value>compared_param.double_value ) return 1; else if(this_param.type<compared_param.type || this_param.integer_value<compared_param.integer_value || this_param.string_value<compared_param.string_value || this_param.double_value<compared_param.double_value ) return -1; else return -1; } public: //--- Creates the calculation part of the indicator, returns the handle int CreateIndicator(void); //--- (1) Calculates the indicator, (2) fills the passed buffer array (taking into account the chart period/symbol) with data from the indicator calculation buffer of this class bool Calculate(void); bool DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[]); //--- (1) Sets (2) returns the initializing value for the specified buffer void SetBufferInitValue(const uint buffer_num,const double value); double BufferInitValue(const uint buffer_num) const; //--- (1) Sets (2) returns the offset value for the specified buffer void SetBufferShift(const uint buffer_num,const int value); double BufferShift(const uint buffer_num) const; //--- Returns data of the specified buffer (1) as is, (2) relative to the specified symbol/timeframe, //--- (3) amount of data in the specified buffer, (4) the state of the indicator line as it is in the calculation part buffer, //--- (5) indicator line state taking into account the chart symbol/period, description of the line state (6) as is in the buffer (7) taking into account the chart symbol/period double GetData(const uint buffer_num,const int index) const; double GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const; uint DataTotal(const uint buffer_num) const; ENUM_LINE_STATE BufferLineState(const uint buffer_num,const int index) const; ENUM_LINE_STATE BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const int index) const; ENUM_LINE_STATE BufferLineStateRelative(const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE); ENUM_LINE_STATE BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE); //--- Returns (1) success flag, (2) calculation error type bool IsSuccess(void) const { return this.m_success; } ENUM_ERR_TYPE TypeError(void) const { return this.m_type_err; } //--- Sets (1) identifier, (2) Digits, (3) custom description, (4) description of the specified buffer void SetID(const int id) { this.m_id=id; } void SetDigits(const uint digits) { this.m_digits=(int)digits; } void SetDescription(const string descr) { this.m_description=descr; } void SetBufferDescription(const uint buffer_num,const string descr); //--- Sets the indexing of buffer arrays of the calculation part not as in the timeseries void SetAsSeriesOff(void); //--- Returns flag of whether the buffer is set as series, (2) historical data for symbol/period is synchronized bool IsSeries(const uint buffer_num) const; bool IsSynchronized(void) const { return (bool)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_SYNCHRONIZED); } //--- Returns (1) timeframe, (2) symbol, (3) name, (4) list of parameters, (5) handle, (6) Digits //--- number of (7) buffers, (8) bars, (9) identifier, (10) description, (11) title, (12) category, //--- (13) number of parameters, (14) program type, description of (15) category, (16) indicator buffer ENUM_TIMEFRAMES Timeframe(void) const { return this.m_timeframe; } string Symbol(void) const { return this.m_symbol; } string Name(void) const { return this.m_name; } string Parameters(void) const { return this.m_parameters; } int Handle(void) const { return this.m_handle; } int Digits(void) const { return this.m_digits; } uint BuffersTotal(void) const { return this.m_buffers.Size(); } uint RatesTotal(void) const { return this.m_rates_total; } int ID(void) const { return this.m_id; } string Description(void) const { return this.m_description; } string Title(void) const { return this.m_title; } ENUM_IND_CATEGORY Category(void) const { return this.m_category; } uint ParamsTotal(void) const { return this.m_param.Size(); } ENUM_PROGRAM_TYPE Program(void) const { return this.m_program; } string CategoryDescription(void); string BufferDescription(const uint buffer_num); //--- Returns (1) structure of parameters by index from array, (2) flag of indicator program, (3) timeframe description MqlParam GetMqlParam(const int index) const { return this.m_param[index]; } bool IsIndicator() const { return(this.Program()==PROGRAM_INDICATOR); } string TimeframeDescription(void) const { return ::StringSubstr(::EnumToString(this.m_timeframe),7); } //--- Returns amount of calculated data int Calculated(void) const { return ::BarsCalculated(this.m_handle); } //--- Virtual method returning the type of object (indicator) virtual int Type(void) const { return this.m_type; } //--- Virtual method for comparing two objects virtual int Compare(const CObject *node,const int mode=0) const { const CIndMSTF *compared=node; switch(mode) { case COMPARE_MODE_ID : return(this.ID()>compared.ID() ? 1 : this.ID()<compared.ID() ? -1 : 0); case COMPARE_MODE_HANDLE : return(this.Handle()>compared.Handle() ? 1 : this.Handle()<compared.Handle() ? -1 : 0); case COMPARE_MODE_CATEGORY : return(this.Category()>compared.Category() ? 1 : this.Category()<compared.Category() ? -1 : 0); case COMPARE_MODE_SYMBOL : return(this.Symbol()>compared.Symbol() ? 1 : this.Symbol()<compared.Symbol() ? -1 : 0); case COMPARE_MODE_TIMEFRAME : return(this.Timeframe()>compared.Timeframe() ? 1 : this.Timeframe()<compared.Timeframe() ? -1 : 0); case COMPARE_MODE_DESCRIPTION : return(this.Description()>compared.Description() ? 1 : this.Description()<compared.Description() ? -1 : 0); //--- Equality of all object parameters default : return(this.IsEqualIndicators(compared) ? 0 : -1); } } //--- Returns the flag of equality of parameters of two indicator objects bool IsEqualIndicators(const CIndMSTF *compared) const { if(this.Type()!=compared.Type() || this.ParamsTotal()!=compared.ParamsTotal()) return false; bool res=true; int total=(int)this.ParamsTotal(); for(int i=0;i<total;i++) res &=this.IsEqualParameters(this.m_param[i],compared.GetMqlParam(i)); res &=(this.Timeframe()==compared.Timeframe()); res &=(this.Symbol()==compared.Symbol()); return res; } //--- Timer void OnTimer(void); //--- Constructor/destructor CIndMSTF(){} CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe); ~CIndMSTF(); };
Der Zweck der einzelnen Variablen und Methoden wird hier im Klassencode kommentiert. Betrachten wir nun die Implementierung von Methoden.
Konstruktor der Klasse:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CIndMSTF::CIndMSTF(const ENUM_INDICATOR type,const uint buffers,const string symbol,const ENUM_TIMEFRAMES timeframe) { //--- Start the timer ::ResetLastError(); if(!::EventSetTimer(1)) ::PrintFormat("%s: EventSetTimer failed. Error %lu",__FUNCTION__,::GetLastError()); //--- Set properties to the values passed to the constructor or to default values this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE); this.m_type=type; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_timeframe=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe); this.m_handle=INVALID_HANDLE; this.m_digits=::Digits(); this.m_success=true; this.m_type_err=ERR_TYPE_NO_ERROR; //--- Set the size of the buffer structure array to the number of indicator buffers ::ResetLastError(); if(::ArrayResize(this.m_buffers,buffers)!=buffers) ::PrintFormat("%s: Buffers ArrayResize failed. Error %lu"__FUNCTION__,::GetLastError()); //--- Set the "empty" value for each buffer by default (can be changed it later) for(int i=0;i<(int)this.m_buffers.Size();i++) this.SetBufferInitValue(i,EMPTY_VALUE); //--- Set initial values of variables involved in resource-efficient calculation of the indicator this.m_prev_calculated=0; this.m_limit=0; this.m_rates_total=::Bars(this.m_symbol,this.m_timeframe); //--- If the indicator is calculated on non-current chart, request data from the required chart //--- (the first access to data starts data pumping) datetime array[]; if(symbol!=::Symbol() || timeframe!=::Period()) ::CopyTime(this.m_symbol,this.m_timeframe,0,this.m_rates_total,array); }
Der Typ des Indikators, die Anzahl seiner Puffer, der Name des Symbols und der Zeitrahmen des Charts werden an den Konstruktor der Klasse übergeben. Als nächstes werden die Variablen auf Standardwerte gesetzt, die Größe des Pufferarrays wird festgelegt, und die Pufferarrays erhalten einen Standardinitialisierungswert von EMPTY_VALUE. Wenn das Indikatorobjekt auf dem nicht aktuellen Chart berechnet wird, rufen wir am Ende des Konstruktors eine Funktion auf, die das Herunterladen der Zeitreihendaten vom Server startet, auf denen der Indikator berechnet wird.
Der Destruktor der Klasse:
//+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CIndMSTF::~CIndMSTF() { //--- Delete timer ::EventKillTimer(); //--- Release indicator handle ::ResetLastError(); if(this.m_handle!=INVALID_HANDLE && !::IndicatorRelease(this.m_handle)) ::PrintFormat("%s: %s, handle %ld IndicatorRelease failed. Error %ld",__FUNCTION__,this.Title(),m_handle,::GetLastError()); //--- Free up the memory of buffer arrays for(int i=0;i<(int)this.BuffersTotal();i++) ::ArrayFree(this.m_buffers[i].array); }
Im Destruktor der Klasse wird der Timer zerstört, der Indikator-Handle freigegeben und der Speicher der Puffer-Arrays freigegeben.
Timer:
//+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CIndMSTF::OnTimer(void) { //--- If the indicator symbol and timeframe match those of the current chart, exit if(this.Symbol()==::Symbol() && this.Timeframe()==::Period()) return; //--- Declare time counter variable static int count1=0; static int count2=0; //--- If the counter of timer 1 has reached the specified value, if(count1>=TIMER_COUNT_1) { //--- call the CopyTime function (timeseries hold) and reset the counter datetime array[1]; ::CopyTime(this.m_symbol,this.m_timeframe,0,1,array); count1=0; } //--- If the counter of timer 2 has reached the specified value if(count2>=TIMER_COUNT_2) { static int count=0; //--- if the previous indicator calculation failed, emulate a tick and print the relevant log if(!this.m_success) { if(::ChartSetSymbolPeriod(0,::Symbol(),::Period())) { count++; ::PrintFormat("%s::%s: Tick emulation. Attempt %ld of 3 ...",__FUNCTION__,this.Title(),count); if(count>2) { count=0; this.m_success=true; } } } //--- reset the counter count2=0; } //--- Increase timer counters count1++; count2++; }
Die Klasse Timer enthält zwei Zähler: einen für die Aufrechterhaltung der Zeitreihe des Indikators und einen zweiten für die Emulation von Ticks an Wochenenden. Wenn der Indikator mit Daten des aktuellen Chartsymbols/Zeitrahmens berechnet wird, wird der Timer nicht verwendet.
Das Indikatorobjekt selbst ist kein Indikator. Es ist lediglich eine Hülle für den Berechnungsteil des Indikators, die es Ihnen ermöglicht, ihn zu verwalten, Daten von ihm zu empfangen und Daten an das Programm zu übergeben. Im Multi-Indikator-Objekt müssen wir den Indikator selbst erstellen. Dazu verwenden wir die Methode zur Erstellung des Berechnungsteils des Indikators, die das bei der Erstellung erhaltene Handle zurückgibt:
//+------------------------------------------------------------------+ //| Creates an indicator, returns a handle | //+------------------------------------------------------------------+ int CIndMSTF::CreateIndicator(void) { //--- Create indicator name to print to logs string name=::StringFormat("%s(%s,%s)",::StringSubstr(::EnumToString(this.m_type),4),this.m_symbol,this.TimeframeDescription()); //--- Create the calculation part of the indicator ::ResetLastError(); this.m_handle=::IndicatorCreate(this.m_symbol,this.m_timeframe,this.m_type,this.m_param.Size(),this.m_param); //--- If the calculation part is not created, log a message about the failed creation of the indicator if(this.m_handle==INVALID_HANDLE) ::PrintFormat("%s: Failed to create indicator handle %s. Error %ld",__FUNCTION__,name,::GetLastError()); //--- If the indicator is created, set non-timeseries indexing of indicator arrays else this.SetAsSeriesOff(); //--- Return the handle of the created indicator or -1 if unsuccessful return this.m_handle; }
Puffer eines Indikatorobjekts sind dynamische Arrays innerhalb der oben beschriebenen Pufferstruktur. Ihre Größe sollte geändert werden, um der ständig wachsenden Menge an neuen Daten Rechnung zu tragen. Hierfür gibt es zwei Methoden: eine Methode zum Ändern der Größe des angegebenen Pufferarrays und eine Methode zum Ändern der Größe aller Puffer des Indikatorobjekts auf einmal.
Methode zur Größenänderung des angegebenen Indikatorpuffers:
//+------------------------------------------------------------------+ //| Resize the specified indicator buffer | //+------------------------------------------------------------------+ bool CIndMSTF::BufferResize(const uint buffer_num,const int new_buff_size) { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false' if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return false; } //--- Resize the buffer ::ResetLastError(); bool res=this.m_buffers[buffer_num].BuffResize(new_buff_size); //--- If unsuccessful, print a message to the log if(!res) ::PrintFormat("%s::%s: Buffer(%lu) resize failed. Error %lu",__FUNCTION__,this.Title(),buffer_num,::GetLastError()); //--- Return the result of resizing the buffer array return res; }
Die Methode erhält als Parameter die Nummer des Puffers, dessen Array um den ebenfalls an die Methode übergebenen Wert verkleinert werden soll. Ist die Puffernummer falsch angegeben, wird eine entsprechende Meldung protokolliert und die Methode gibt false zurück.
Wenn die Größenänderung des Arrays fehlgeschlagen ist, wird eine Meldung im Protokoll ausgegeben. Das Ergebnis der Größenänderung des Pufferarrays wird schließlich zurückgegeben.
Methode zur Größenänderung aller Indikatorpuffer:
//+------------------------------------------------------------------+ //| Resize all indicator buffers | //+------------------------------------------------------------------+ bool CIndMSTF::BuffersResize(const int new_buff_size) { //--- In a loop through all indicator buffers, add to the 'res' variable the resizing result of each next buffer bool res=true; int total=(int)this.BuffersTotal(); for(int i=0;i<total;i++) res &=this.m_buffers[i].BuffResize(new_buff_size); //--- Return the result of resizing all arrays of indicator buffers return res; }
Hier wird in einer Schleife durch alle Puffer des Indikatorobjekts das Ergebnis der Größenänderung des nächsten Pufferarrays zur Variablen „res“ hinzugefügt, deren Wert schließlich von der Methode zurückgegeben wird.
Methoden zur Initialisierung von Arrays von Indikatorobjektpuffern sind auf ähnliche Weise organisiert.
Methode, die den angegebenen Indikatorpuffer initialisiert:
//+------------------------------------------------------------------+ //| Initialize the specified indicator buffer | //+------------------------------------------------------------------+ bool CIndMSTF::BufferInitialize(const uint buffer_num,const int new_buff_size) { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false' if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return false; } //--- resizing the buffer array bool res=this.BufferResize(buffer_num,new_buff_size); //--- If successfully resized, initialize the buffer with the set initializing value if(res) this.m_buffers[buffer_num].InitBuffer(); //--- Return the result return res; }
Hier wird auch die Korrektheit der Nummer des initialisierten Puffers überprüft. Der Puffer wird dann auf eine neue Größe gesetzt, und wenn die Größenänderung erfolgreich war, wird das Array mit dem für diesen Puffer festgelegten Wert initialisiert.
Methode zur Initialisierung alle Indikatorpuffer initialisiert:
//+------------------------------------------------------------------+ //| Initialize all indicator buffers | //+------------------------------------------------------------------+ bool CIndMSTF::BuffersInitialize(const int new_buff_size) { //--- In a loop through all indicator buffers, add to the 'res' variable the resizing result of each next buffer //--- If successfully resized, initialize the buffer with the set initializing value bool res=true; int total=(int)this.BuffersTotal(); for(int i=0;i<total;i++) { res &=this.m_buffers[i].BuffResize(new_buff_size); if(res) this.m_buffers[i].InitBuffer(); } //--- Return the overall result return res; }
In einer Schleife durch alle Puffer des Indikatorobjekts wird das Ergebnis der Größenänderung des nächsten Pufferarrays zur Variablen „res“ hinzugefügt. Nach erfolgreicher Größenänderung wird der Puffer mit dem für ihn festgelegten Initialisierungswert initialisiert. Die Methode gibt den Endzustand der Variablen „res“ zurück, die den Wert “false“ hat, wenn mindestens einer der Puffer nicht initialisiert werden konnte.
Die übrigen Methoden zum Setzen und Zurückgeben von Pufferwerten sind mit den oben beschriebenen identisch:
//+------------------------------------------------------------------+ //| Set the initializing value for the specified buffer | //+------------------------------------------------------------------+ void CIndMSTF::SetBufferInitValue(const uint buffer_num,const double value) { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return; } //--- Set a new initializing value for the specified buffer this.m_buffers[buffer_num].SetInitValue(value); } //+------------------------------------------------------------------+ //| Return the initialization value of the specified buffer | //+------------------------------------------------------------------+ double CIndMSTF::BufferInitValue(const uint buffer_num) const { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); //--- If the indicator has buffers, return the initializing value of the first one, otherwise EMPTY_VALUE return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE); } //--- Return the initializing value of the requested buffer return this.m_buffers[buffer_num].InitValue(); } //+------------------------------------------------------------------+ //| Sets the offset value for the specified buffer | //+------------------------------------------------------------------+ void CIndMSTF::SetBufferShift(const uint buffer_num,const int value) { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return; } //--- Set the offset value for the buffer this.m_buffers[buffer_num].SetShift(value); } //+------------------------------------------------------------------+ //| Return the offset value of the specified buffer | //+------------------------------------------------------------------+ double CIndMSTF::BufferShift(const uint buffer_num) const { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); //--- If the indicator has buffers, return the shift value of the very first one, otherwise 0 return(this.BuffersTotal()>0 ? this.m_buffers[0].Shift() : 0); } //--- Return the offset value of the requested buffer return this.m_buffers[buffer_num].Shift(); }
Die Logik der Methoden ist in den Kommentaren zum Code detailliert beschrieben.
Methode zur Berechnung der Indikatordaten. Der berechnete Indikator (sein berechneter Teil) hat einen Puffer, der alle berechneten Indikatordaten enthält. Wir müssen Daten aus dem Berechnungsteilpuffer in die Pufferfelder des Indikatorobjekts kopieren. Für diesen Vorgang müssen wir eine ressourceneffiziente Berechnung organisieren: Wir werden den gesamten Puffer nur beim ersten Start oder bei einer Änderung der historischen Daten kopieren.
Wir haben eine solche Berechnung oben besprochen, und sie sollte in der Berechnungsmethode des Indikatorobjekts organisiert werden. Im Falle von Fehlern gibt die Methode false zurück, und das aufrufende Programm reagiert mit dem Beenden des Handlers (OnTick in einem Expert Advisor und OnCalculate in einem Indikator), bevor der nächste Tick eintrifft. Fehler, die eine Rückkehr aus der Methode erfordern, werden als Beginn des Herunterladens historischer Daten, unvollständiges Herunterladen der Historie, unvollständige Indikatorberechnung und Fehler beim Kopieren von Daten aus dem Berechnungsteilpuffer in den Indikatorobjektpuffer betrachtet. Die Methode schreibt den Fehlercode in eine Variable, sodass der Aufrufer ihn lesen und korrekt verarbeiten kann.
Methode, die die Puffer der Indikatorobjekte mit Daten aus dem Berechnungsteilpuffer füllt:
//+------------------------------------------------------------------+ //| Fill object buffers with data from the calculation part buffer | //+------------------------------------------------------------------+ bool CIndMSTF::Calculate(void) { //--- Set the success flag to true, and the error type to no error this.m_success=true; this.m_type_err=ERR_TYPE_NO_ERROR; //--- If the data is not yet synchronized with the trade server, if(!this.IsSynchronized()) { //--- Log a message about non-synchronized data, ::PrintFormat("%s::%s: Waiting for data to sync...",__FUNCTION__,this.Title()); //--- set the error type, add 'false' to the error flag and return 'false' this.m_type_err=ERR_TYPE_NO_CYNC; this.m_success &=false; return false; } //--- If the Calculated method returned -1, this means the start of data downloading if(this.Calculated()==WRONG_VALUE) { //--- Log a message about the start of data downloading, ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription()); //--- set the error type, add 'false' to the error flag and return 'false' this.m_type_err=ERR_TYPE_NO_DATA; this.m_success &=false; return false; } //--- If the Calculated method returned 0, this means that the indicator has not yet been calculated if(this.Calculated()==0) { //--- Log a message about waiting for the indicator to be calculated, ::PrintFormat("%s::%s: Waiting for a new tick and when the indicator will be calculated...",__FUNCTION__,this.Title()); //--- set the error type, add 'false' to the error flag and return 'false' this.m_type_err=ERR_TYPE_NO_CALC; this.m_success &=false; return false; } //--- Get the number of data bars for the indicator symbol/period int bars=::Bars(this.m_symbol,this.m_timeframe); //--- If the Bars function returned a zero value, which often happens on weekends, calculate the available number of bars if(bars==0) { //--- Get the date of the very first available bar in history for the symbol/period datetime firstdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_FIRSTDATE); //--- Get the date of the last (current) bar in history for the symbol/period datetime lastdate=(datetime)::SeriesInfoInteger(this.m_symbol,this.m_timeframe,SERIES_LASTBAR_DATE); //--- Calculate the number of bars between the first and last dates of history int sec=::PeriodSeconds(this.m_timeframe); ulong date_bars=(((ulong)lastdate-(ulong)firstdate)/(sec>0 ? sec : 1))+1; //--- Write to the 'bars' variable the smaller value of the calculated number of bars and the maximum number of bars available in the terminal bars=(int)fmin(date_bars,::TerminalInfoInteger(TERMINAL_MAXBARS)); } //--- Write the resulting number of available bars to m_rates_total if(this.m_rates_total!=bars) this.m_rates_total=bars; //--- If the number of available bars is received, and it is 2 or less, if(this.m_rates_total>=0 && this.m_rates_total<3) { //--- Log a message about the number of available bars being too small ::PrintFormat("%s::%s: Not enough data for calculation: %ld bars. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_rates_total); //--- set the error type, add 'false' to the error flag and return 'false' this.m_type_err=ERR_TYPE_NO_DATA; this.m_success &=false; return false; } //--- Calculate the number of bars required to calculate the indicator //--- Either the entire available history, or 1 when a new bar opens, or 0 on the current tick this.m_limit=this.m_rates_total-this.m_prev_calculated; this.m_prev_calculated=this.Calculated(); //--- Declare an array of size 2 to receive data into it from the indicator's calculation part buffer //--- We always get two bars: previous and current double array[2]; //--- Get the number of indicator buffers int total=(int)this.BuffersTotal(); //--- If the calculated m_limit is greater than 1, it means either the first launch or changes in historical data //--- In this case, a complete recalculation of the indicator is necessary if(this.m_limit>1) { //--- In a loop over the number of indicator buffers for(int i=0;i<total;i++) { //--- resize the indicator buffer array and initialize it to the "empty" value set for this buffer this.BufferInitialize(i,this.m_rates_total); ::ResetLastError(); //--- Copy all available historical data from indicator's calculation part array to buffer array of indicator object int copied=::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),this.m_rates_total,this.m_buffers[i].array); //--- If not all data is copied if(copied!=this.m_rates_total) { //--- If CopyBuffer returned -1, this means the start of historical data downloading //--- print a message about this to the log if(copied==WRONG_VALUE) ::PrintFormat("%s::%s: Start downloading data by %s/%s. Waiting for the next tick...",__FUNCTION__,this.Title(),this.m_symbol,this.TimeframeDescription()); //--- In any other case, not all data has been copied yet //--- print a message about this to the log else ::PrintFormat("%s::%s: Not all data was copied. Data available: %lu, total copied: %ld",__FUNCTION__,this.Title(),this.m_rates_total,copied); //--- Write the absence of data to the error type this.m_type_err=ERR_TYPE_NO_DATA; //--- Add 'false' to the result and return 'false' to exit the method and wait for the next tick this.m_success &=false; return false; } } //--- If we exited the loop of copying all indicator buffers, then everything was successful - return 'true' return true; } //--- If calculated m_limit is less than or equal to 1, this means either opening of a new bar (m_limit==1) or current tick (m_limit==0) //--- In this case, it is necessary to calculate two bars - the first and the current if(this.m_limit<=1) { //--- In a loop over the number of indicator buffers for(int i=0;i<total;i++) { //--- If this is the opening of a new bar and resizing the indicator buffer failed, if(this.m_limit==1 && !this.BufferResize(i,this.m_rates_total)) { //--- add 'false' to the m_success variable and return 'false' //--- Here, an error message will be printed to log from the BufferResize method this.m_success &=false; return false; } //--- If failed to copy two bars from the indicator's calculation part buffer, ::ResetLastError(); if(::CopyBuffer(this.m_handle,i,-this.m_buffers[i].Shift(),2,array)!=2) { //--- report this via the log, add 'false' to the m_success variable and return 'false' ::PrintFormat("%s::%s: CopyBuffer(%lu) failed. Error %lu",__FUNCTION__,this.Title(),i,::GetLastError()); this.m_success &=false; return false; } //--- If got here, it means copying was successful - //--- copy data from array[], into which the last two bars were copied, to the indicator object buffer this.m_buffers[i].array[this.DataTotal(i)-1]=array[1]; this.m_buffers[i].array[this.DataTotal(i)-2]=array[0]; } //--- Success return true; } //--- Undefined 'limit' option - return 'false' return false; }
Die gesamte Logik der Methode ist in den Kommentaren zu den einzelnen Codeblöcken detailliert beschrieben. Die Methode wird vom Programm aus aufgerufen und, wenn sie false zurückgibt, OnTick oder OnCalculate vor dem nächsten Tick verlassen.
Wenn die Methode ohne Fehler abgeschlossen wird, enthält der Puffer des Indikatorobjekts Daten, die zur Verwendung bereit sind. Auf diese Daten kann mit Methoden zugegriffen werden, die im Folgenden erläutert werden.
Indikatoren verfügen über spezielle Methoden zur Ausgabe von Daten aus dem durch diese Methode gefüllten Puffer des Indikatorobjekts in die Puffer der Indikatordarstellungen. Die Methoden geben die Daten in einen Zeichnungspuffer aus, entweder in der gleichen Form, in der die Daten in den Puffer des Indikatorobjekts geschrieben werden, oder unter Berücksichtigung des Chartsymbols/der Periode. Diese Methoden können verwendet werden, um berechnete Daten in einem Indikatorobjekt im aktuellen Chart anzuzeigen.
Methode, die das übergebene Array mit Daten aus dem Klassenpuffer füllt:
//+------------------------------------------------------------------+ //| Fill the passed array with data from the class buffer | //+------------------------------------------------------------------+ bool CIndMSTF::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int limit,double &buffer[]) { //--- Set the success flag this.m_success=true; //--- Get the indexing direction of the buffer array passed to the method and, //--- if non-timeseries indexing, set timeseries indexing bool as_series=::ArrayGetAsSeries(buffer); if(!as_series) ::ArraySetAsSeries(buffer,true); //--- Set the symbol name and timeframe value passed to the method string symbol=(symbol_to=="" || symbol_to==NULL ? ::Symbol() : symbol_to); ENUM_TIMEFRAMES timeframe=(timeframe_to==PERIOD_CURRENT ? ::Period() : timeframe_to); datetime array[2]; //--- If this is the first launch or history changes, initialize the buffer array passed to the method if(limit>1 && this.m_limit>1) { ::PrintFormat("%s::%s First start, or historical data has been changed. Initialize Buffer(%lu)",__FUNCTION__,this.Title(),buffer_num); ::ArrayInitialize(buffer,this.BufferInitValue(buffer_num)); } //--- Set the value of the loop counter (no more than the maximum number of bars in the terminal on the chart) int count=(limit<=1 ? 2 : ::fmin(::TerminalInfoInteger(TERMINAL_MAXBARS),limit)); //--- In a loop from the zero bar to the value of the loop counter for(int i=0;i<count;i++) { //--- If the chart timeframe matches the class object timeframe, fill the buffer directly from the class object array if(timeframe==::Period() && this.m_timeframe==::Period()) buffer[i]=this.GetData(buffer_num,i); //--- Otherwise, if the chart timeframe is not equal to the timeframe of the class object else { //--- Find out which time of this class the bar of the current chart timeframe, corresponding to the loop index, belongs to ::ResetLastError(); if(::CopyTime(symbol,timeframe,i,2,array)!=2) { //--- If there is no data in the terminal, move on if(::GetLastError()==4401) continue; //--- Error in obtaining existing data - return false this.m_success &=false; return false; } //--- Using time of bar of current chart timeframe, find corresponding index of bar of class object's chart period ::ResetLastError(); int bar=::iBarShift(this.m_symbol,this.m_timeframe,array[0]); if(bar==WRONG_VALUE) { this.m_success &=false; continue; } //--- If this is historical data (not the first or zero bar) - //--- in the indicator buffer at the loop index, write the value obtained from the calculation part buffer if(i>1) buffer[i]=this.GetData(buffer_num,bar); //--- If this is the current (zero) or previous (first) bar else { //--- Get the time of bars 0 and 1 by symbol/timeframe of the class object if(::CopyTime(this.m_symbol,this.m_timeframe,0,2,array)!=2) { this.m_success &=false; return false; } //--- Using time, get indexes of current and previous bars on the chart whose symbol/period was passed to method int bar0=::iBarShift(symbol,timeframe,array[1]); int bar1=::iBarShift(symbol,timeframe,array[0]); if(bar0==WRONG_VALUE || bar1==WRONG_VALUE) { this.m_success &=false; return false; } //--- If the chart timeframe is lower than the timeframe of the class object, if(timeframe<this.m_timeframe) { //--- in a loop from bar with smaller time to current chart bar, fill the buffer with data from the last 2 cells of the indicator buffer array for(int j=bar1;j>=0;j--) buffer[j]=this.GetData(buffer_num,(j>bar0 ? 1 : 0)); } //--- If the chart timeframe is higher than the timeframe of the class object, else { //--- Get the time of the current and previous bars by symbol/timeframe of the current chart if(::CopyTime(symbol,timeframe,0,2,array)!=2) { this.m_success &=false; return false; } //--- Using time, get indexes of bars in indicator's calculation part buffer, corresponding to time of current and previous bars on the chart int bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[1]); int bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]); //--- Write into indicator buffer, at indexes 1 and 0, values from corresponding indexes of calculation part buffer buffer[1]=this.GetData(buffer_num,bar1); buffer[0]=this.GetData(buffer_num,bar0); } } } } //--- Set initial indexing of the buffer array passed to the method ::ArraySetAsSeries(buffer,as_series); //--- Successful return true; }
Die Logik der Methode wird in ihrem Listing detailliert beschrieben. Die Idee der Methode besteht in der korrekten Berechnung der Balken des aktuellen Charts, die mit Daten aus dem Indikatorpuffer-Array gefüllt werden müssen, das für einen anderen Zeitrahmen berechnet wurde. Der letzte Parameter, der an die Methode übergeben wird, ist ein Array eines nutzerdefinierten Indikator-Zeichnungspuffer, der den auf ein anderes Symbol/eine andere Periode berechneten Indikator darstellen soll.
Eine Methode, die die Daten des angegebenen Puffers so zurückgibt, wie sie sind:
//+------------------------------------------------------------------+ //| Return the data of the specified buffer as is | //+------------------------------------------------------------------+ double CIndMSTF::GetData(const uint buffer_num,const int index) const { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to log if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); //--- If the indicator has buffers, return "empty" value of the first one, otherwise EMPTY_VALUE return(this.BuffersTotal()>0 ? this.BufferInitValue(0) : EMPTY_VALUE); } //--- If an incorrect index is specified, return the "empty" value of the specified buffer if(index<0 || index>(int)this.DataTotal(buffer_num)-1) return this.BufferInitValue(buffer_num); //--- Calculate the real index in the buffer array and return the value at this index int n=int(this.DataTotal(buffer_num)-1-index); return this.m_buffers[buffer_num].array[n]; }
Die Methode gibt einfach Daten aus dem Puffer des Indikatorobjekts am angegebenen Index zurück.
Methode, die Folgendes zurückgibt Daten des angegebenen Puffers für das angegebene Symbol/Zeitfenster:
//+-------------------------------------------------------------------+ //| Returns data from specified buffer for specified symbol/timeframe | //+-------------------------------------------------------------------+ double CIndMSTF::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const uint buffer_num,const int index) const { //--- If current symbol/period of the chart is specified if(timeframe_to==::Period() && this.m_timeframe==::Period() && symbol_to==::Symbol() && this.m_symbol==::Symbol()) return this.GetData(buffer_num,index); //--- Find out which time of this class the current chart timeframe's bar, corresponding to the loop index, belongs to datetime array[]; if(::CopyTime(symbol_to,timeframe_to,index,1,array)!=1) return this.BufferInitValue(buffer_num); //--- Using time of bar of current chart timeframe, find corresponding bar index of bar this class chart period int bar=iBarShift(this.m_symbol,this.m_timeframe,array[0]); //--- If the bar is not found, return the "empty" value set for the buffer if(bar==WRONG_VALUE) return this.BufferInitValue(buffer_num); //--- Return value from the indicator object buffer at the found index return this.GetData(buffer_num,bar); }
Die Methode findet den Balkenindex der Zeitreihe, auf der der Indikator, der dem an die Methode übergebenen Chartsymbol/der Periode entspricht, berechnet wird, und gibt Daten aus dem Puffer des Indikatorobjekts bei dem gefundenen Index zurück.
Die Methode, die den Zustand der Indikatorlinie zurückgibt:
//+------------------------------------------------------------------+ //| Return the state of the indicator line as is | //+------------------------------------------------------------------+ ENUM_LINE_STATE CIndMSTF::BufferLineState(const uint buffer_num,const int index) const { //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index const double value0=this.GetData(buffer_num,index); const double value1=this.GetData(buffer_num,index+1); const double value2=this.GetData(buffer_num,index+2); //--- If at least one of the values could not be obtained, return an undefined value if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE) return LINE_STATE_NONE; //--- Line upward reversal (value2>value1 && value0>value1) if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0) return LINE_STATE_TURN_UP; //--- Line upward direction (value2<=value1 && value0>value1) else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0) return LINE_STATE_UP; //--- Line upward stop (value2<=value1 && value0==value1) else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0) return LINE_STATE_STOP_UP; //--- Line downward reversal (value2<value1 && value0<value1) if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0) return LINE_STATE_TURN_DOWN; //--- Line downward direction (value2>=value1 && value0<value1) else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0) return LINE_STATE_DOWN; //--- Line downward stop (value2>=value1 && value0==value1) else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0) return LINE_STATE_STOP_DOWN; //--- Undefined state return LINE_STATE_NONE; }
Die Methode ermittelt den Linienzustand des Indikatorobjekts anhand der Daten in seinem Puffer und gibt den gefundenen Wert zurück.
Methode, die den Zustand des Indikators für ein bestimmtes Symbol/eine bestimmte Periode zurückgibt:
//+------------------------------------------------------------------+ //| Return indicator line state for the specific symbol/period | //+------------------------------------------------------------------+ ENUM_LINE_STATE CIndMSTF::BufferLineState(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const uint buffer_num,const int index) const { //--- Determine the chart symbol/period passed to the method string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from); ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from); //--- If we get data from symbol/period equal to current chart, return state from the buffer "as is" if(symbol==::Symbol() && symbol==this.m_symbol && timeframe==::Period() && timeframe==this.m_timeframe) return this.BufferLineState(buffer_num,index); //--- Declare variables to search for the required bars on the current chart datetime array[1]; int bar0=WRONG_VALUE; int bar1=WRONG_VALUE; int bar2=WRONG_VALUE; //--- Get the time of the first bar on the chart ::ResetLastError(); if(::CopyTime(symbol,timeframe,index,1,array)!=1) { ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index,::GetLastError()); return LINE_STATE_NONE; } //--- Get index of the first bar in indicator object buffer based on bar opening time on the chart bar0=::iBarShift(this.m_symbol,this.m_timeframe,array[0]); if(bar0==WRONG_VALUE) { ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError()); return LINE_STATE_NONE; } //--- Get the time of the second bar on the chart ::ResetLastError(); if(::CopyTime(symbol,timeframe,index+1,1,array)!=1) { ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+1,::GetLastError()); return LINE_STATE_NONE; } //--- Get index of the second bar in indicator object buffer based on bar opening time on the chart bar1=::iBarShift(this.m_symbol,this.m_timeframe,array[0]); if(bar1==WRONG_VALUE) { ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError()); return LINE_STATE_NONE; } //--- Get the time of the third bar on the chart ::ResetLastError(); if(::CopyTime(symbol,timeframe,index+2,1,array)!=1) { ::PrintFormat("%s: CopyTime for %s/%s, bar %ld failed. Error %lu",__FUNCTION__,symbol,::StringSubstr(::EnumToString(timeframe),7),index+2,::GetLastError()); return LINE_STATE_NONE; } //--- Get index of the third bar in indicator object buffer based on bar opening time on the chart bar2=::iBarShift(this.m_symbol,this.m_timeframe,array[0]); if(bar2==WRONG_VALUE) { ::PrintFormat("%s: iBarShift for %s/%s, time %s failed. Error %lu",__FUNCTION__,this.m_symbol,this.TimeframeDescription(),string(array[0]),::GetLastError()); return LINE_STATE_NONE; } //--- Get the values of the indicator line with the shift (0,1,2) relative to the passed index const double value0=this.GetData(buffer_num,bar0); const double value1=this.GetData(buffer_num,bar1); const double value2=this.GetData(buffer_num,bar2); //--- If at least one of the values could not be obtained, return an undefined value if(value0==EMPTY_VALUE || value1==EMPTY_VALUE || value2==EMPTY_VALUE) return LINE_STATE_NONE; //--- Line upward reversal (value2>value1 && value0>value1) if(::NormalizeDouble(value2-value1,this.m_digits)>0 && ::NormalizeDouble(value0-value1,this.m_digits)>0) return LINE_STATE_TURN_UP; //--- Line upward direction (value2<=value1 && value0>value1) else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)>0) return LINE_STATE_UP; //--- Line upward stop (value2<=value1 && value0==value1) else if(::NormalizeDouble(value2-value1,this.m_digits)<=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0) return LINE_STATE_STOP_UP; //--- Line downward reversal (value2<value1 && value0<value1) if(::NormalizeDouble(value2-value1,this.m_digits)<0 && ::NormalizeDouble(value0-value1,this.m_digits)<0) return LINE_STATE_TURN_DOWN; //--- Line downward direction (value2>=value1 && value0<value1) else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)<0) return LINE_STATE_DOWN; //--- Line downward stop (value2>=value1 && value0==value1) else if(::NormalizeDouble(value2-value1,this.m_digits)>=0 && ::NormalizeDouble(value0-value1,this.m_digits)==0) return LINE_STATE_STOP_DOWN; //--- Undefined state return LINE_STATE_NONE; }
Die Methode wird verwendet, um den Zustand der Objektlinie des Indikators in Bezug auf das aktuelle Chart zu ermitteln. Wenn das Indikatorobjekt mit Daten aus einem höheren Zeitrahmen als dem aktuellen Chart berechnet wird, wird seine Linie über die Balken des aktuellen Charts „gestreckt“. Die Methode ermöglicht es Ihnen, den Zustand der Indikatorlinie auf dem angegebenen Balken des aktuellen Charts korrekt zu ermitteln.
Die Methode, die den den Zustand der Leitung in Bezug auf die angegebene Ebene:
//+------------------------------------------------------------------+ //| Return the state of the line relative to the specified level | //+------------------------------------------------------------------+ ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE) { //--- Get the values of the indicator line with the shift (0,1) relative to the passed index const double value0=this.GetData(buffer_num,index); const double value1=this.GetData(buffer_num,index+1); //--- If at least one of the values could not be obtained, return an undefined value if(value0==EMPTY_VALUE || value1==EMPTY_VALUE) return LINE_STATE_NONE; //--- Define the second level to compare double level=(level1==EMPTY_VALUE ? level0 : level1); //--- The line is below the level (value1<level && value0<level0) if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0) return LINE_STATE_BELOW; //--- The line is above the level (value1>level && value0>level0) if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0) return LINE_STATE_ABOVE; //--- The line crossed the level upwards (value1<=level && value0>level0) if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0) return LINE_STATE_CROSS_UP; //--- The line crossed the level downwards (value1>=level && value0<level0) if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0) return LINE_STATE_CROSS_DOWN; //--- The line touched the level from below (value1<level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_TOUCH_BELOW; //--- The line touched the level from above (value1>level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_TOUCH_BELOW; //--- Line is equal to the level value (value1==level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_EQUALS; //--- Undefined state return LINE_STATE_NONE; }
Die Methode, die Folgendes zurückgibt den Zustand der Linie in Bezug auf das angegebene Niveau auf dem angegebenen Chartsymbol/der angegebenen Periode zurückgibt:
//+------------------------------------------------------------------+ //| Return the state of the line relative to the specified level | //| on the specified chart symbol/period | //+------------------------------------------------------------------+ ENUM_LINE_STATE CIndMSTF::BufferLineStateRelative(const string symbol_from,const ENUM_TIMEFRAMES timeframes_from,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE) { //--- Determine the chart symbol/period passed to the method string symbol=(symbol_from=="" || symbol_from==NULL ? ::Symbol() : symbol_from); ENUM_TIMEFRAMES timeframe=(timeframes_from==PERIOD_CURRENT ? ::Period() : timeframes_from); //--- Get the values of the indicator line with the shift (0,1) relative to the passed index const double value0=this.GetDataTo(symbol,timeframe,buffer_num,index); const double value1=this.GetDataTo(symbol,timeframe,buffer_num,index+1); //--- If at least one of the values could not be obtained, return an undefined value if(value0==EMPTY_VALUE || value1==EMPTY_VALUE) return LINE_STATE_NONE; //--- Define the second level to compare double level=(level1==EMPTY_VALUE ? level0 : level1); //--- The line is below the level (value1<level && value0<level0) if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)<0) return LINE_STATE_BELOW; //--- The line is above the level (value1>level && value0>level0) if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)>0) return LINE_STATE_ABOVE; //--- The line crossed the level upwards (value1<=level && value0>level0) if(::NormalizeDouble(value1-level,this.m_digits)<=0 && ::NormalizeDouble(value0-level0,this.m_digits)>0) return LINE_STATE_CROSS_UP; //--- The line crossed the level downwards (value1>=level && value0<level0) if(::NormalizeDouble(value1-level,this.m_digits)>=0 && ::NormalizeDouble(value0-level0,this.m_digits)<0) return LINE_STATE_CROSS_DOWN; //--- The line touched the level from below (value1<level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)<0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_TOUCH_BELOW; //--- The line touched the level from above (value1>level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)>0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_TOUCH_BELOW; //--- Line is equal to the level value (value1==level0 && value0==level0) if(::NormalizeDouble(value1-level,this.m_digits)==0 && ::NormalizeDouble(value0-level0,this.m_digits)==0) return LINE_STATE_EQUALS; //--- Undefined state return LINE_STATE_NONE; }
Mehr über die Methoden, um den Status der Indikatorlinien zu erhalten, können Sie im Artikel über die Verbindung von Oszillatoren mit EAs lesen.
Andere Methoden der Klasse:
//+------------------------------------------------------------------+ //| Return category description | //+------------------------------------------------------------------+ string CIndMSTF::CategoryDescription(void) { //--- Create a category name from ENUM_IND_CATEGORY and return the resulting text string category=::StringSubstr(::EnumToString(this.m_category),13); if(category.Lower()) category.SetChar(0,ushort(category.GetChar(0)-0x20)); return category; } //+------------------------------------------------------------------+ //| Return the description of the indicator buffer | //+------------------------------------------------------------------+ string CIndMSTF::BufferDescription(const uint buffer_num) { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return empty string if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return ""; } //--- If indicator has buffers, return description of the specified buffer, otherwise description of the indicator return(this.BuffersTotal()>0 ? this.m_buffers[buffer_num].descript : this.m_title); } //+------------------------------------------------------------------+ //| Set indicator buffer description | //+------------------------------------------------------------------+ void CIndMSTF::SetBufferDescription(const uint buffer_num,const string descr) { //--- If the indicator has no buffers, exit if(this.BuffersTotal()==0) return; //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and exit if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return; } //--- Write the description passed to the method into the specified buffer this.m_buffers[buffer_num].descript=descr; } //+------------------------------------------------------------------+ //| Disable timeseries indexing of buffer arrays | //+------------------------------------------------------------------+ void CIndMSTF::SetAsSeriesOff(void) { //--- In a loop through all indicator buffers, disable the array as timeseries flag for(int i=0;i<(int)this.BuffersTotal();i++) ::ArraySetAsSeries(this.m_buffers[i].array,false); } //+------------------------------------------------------------------+ //| Returns the timeseries flag of the given buffer | //+------------------------------------------------------------------+ bool CIndMSTF::IsSeries(const uint buffer_num) const { //--- Validate the buffer number passed to the method and, if the number is incorrect, print a message to the log and return 'false' if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return false; } //--- Return the timeseries flag of the array of the specified buffer return (bool)::ArrayGetAsSeries(this.m_buffers[buffer_num].array); } //+------------------------------------------------------------------+ //| Returns the amount of data in the specified buffer | //+------------------------------------------------------------------+ uint CIndMSTF::DataTotal(const uint buffer_num) const { //--- Validate the buffer number passed to method and, if number is incorrect, print a message to log and return zero if(buffer_num>this.BuffersTotal()-1) { string buff_limit=(this.BuffersTotal()==1 ? "0" : "0 - "+string(this.BuffersTotal()-1)); ::PrintFormat("%s: Invalid buffer number passed (%lu). Value must be %s",__FUNCTION__,buffer_num,buff_limit); return 0; } //--- Return the array size of the specified buffer return this.m_buffers[buffer_num].array.Size(); }
Die Basisklasse des Multi-Symbol-Multi-Perioden-Indikatorobjekts ist fertig. Die Klasse enthält alle notwendigen Funktionen für die Arbeit mit Indikatoren, die auf Zeitreihendaten basieren, die nicht zum aktuellen Chart gehören.
Um verschiedene Arten von technischen Indikatoren zu erstellen, werden von der neu erstellten Basisklasse abgeleitete Klassen verwendet. In den Konstruktoren der abgeleiteten Klassen werden die Parameter und Eigenschaften angegeben, die für den jeweiligen Indikatortyp typisch sind.
Indikatorklassen nach Art
Von der Basisklasse abgeleitete Klassen sind die einfachsten und enthalten nur einen Konstruktor. Dem Klassenkonstruktor werden das Symbol/die Periode des Charts, auf dem der Indikator berechnet wird, und die für diesen Indikatortyp spezifischen Eingabeparameter übergeben. In der Initialisierungszeile des Konstruktors werden Parameter an den Konstruktor der übergeordneten Klasse übergeben.
Ein Beispiel für eine Klasse, die ein Indikatorobjekt erstellt, das keine Parameter enthält:
//+------------------------------------------------------------------+ //| Accelerator Oscillator indicator class | //+------------------------------------------------------------------+ class CIndAC : public CIndMSTF { public: //--- Constructor CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe) { //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("AC"); this.SetDescription("Accelerator Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } };
Der Indikatortyp, die Anzahl der Puffer, das Chart-Symbol und die Chart-Periode, für die der Indikator berechnet werden soll, werden in der Initialisierungszeichenfolge an die übergeordnete Klasse übergeben. Im Klassenkörper wird eine Zeichenkette mit den Eigenschaften der Indikatorparameter erstellt. In diesem Fall, wenn der Indikator auf den Daten des aktuellen Charts erstellt wird, ist der Parameterstring leer. Andernfalls enthält sie das Symbol und die Periode des Charts in der Form, zum Beispiel „(EURUSD,H1)“. Als nächstes werden alle Parameter, die zu diesem Indikatortyp gehören (hier ist es der Accelerator Oscillator Indikator), im Körper des Konstruktors festgelegt.
Für jeden Indikator kann die Anzahl der Dezimalstellen eingestellt werden, die im Datenfenster und im Symboldiagramm angezeigt werden. Der Konstruktor dieser Klasse verfügt nicht über die Einstellung der Dezimalzellen, Digits, da dieser Wert, der den Dezimalstellen des Symbols entspricht, für das der Indikator berechnet wird, im Konstruktor der übergeordneten Klasse festgelegt wird. Wenn es notwendig ist, einen anderen Digits-Wert für einen Indikator zu setzen, sollte dies entweder in den Konstruktoren der Klassen derjenigen Indikatoren geschehen, bei denen sich Digits von dem des Symbols unterscheidet, oder es kann nach der Erstellung des Indikatorobjekts mit der Methode SetDigits() geändert werden.
Eine Indikatorklasse mit Parametern:
//+------------------------------------------------------------------+ //| Accumulation/Distribution indicator class | //+------------------------------------------------------------------+ class CIndAD : public CIndMSTF { public: //--- Constructor CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume // used volume ) : CIndMSTF(IND_AD,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("A/D"); this.SetDescription("Accumulation/Distribution"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_VOLUME; this.m_digits=0; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } };
Das sind die Parameter, die in die Array-Struktur der Indikator-Eingabeparameter MqlParam eingetragen werden sollen. Hier wird auch der Standardwert für die Dezimalstellenanzahl des Indikators Accumulation/Distribution angegeben.
Eine vollständige Liste aller Klassen, die von der Basisklasse des Multi-Symbol-Multi-Perioden-Indikators abgeleitet sind:
//+------------------------------------------------------------------+ //| Accelerator Oscillator indicator class | //+------------------------------------------------------------------+ class CIndAC : public CIndMSTF { public: //--- Constructor CIndAC(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AC,1,symbol,timeframe) { //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("AC"); this.SetDescription("Accelerator Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Accumulation/Distribution indicator class | //+------------------------------------------------------------------+ class CIndAD : public CIndMSTF { public: //--- Constructor CIndAD(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume // used volume ) : CIndMSTF(IND_AD,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("A/D"); this.SetDescription("Accumulation/Distribution"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_VOLUME; this.m_digits=0; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Average Directional Movement Index indicator class | //+------------------------------------------------------------------+ class CIndADX : public CIndMSTF { public: //--- Constructor CIndADX(const string symbol,const ENUM_TIMEFRAMES timeframe, const int adx_period // averaging period ) : CIndMSTF(IND_ADX,3,symbol,timeframe) { // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("ADX"); this.SetDescription("Average Directional Movement Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=2; //--- write descriptions of MAIN_LINE, PLUSDI_LINE and MINUSDI_LINE line buffers this.SetBufferDescription(MAIN_LINE,this.m_title); this.SetBufferDescription(PLUSDI_LINE,"+DI"); this.SetBufferDescription(MINUSDI_LINE,"-DI"); } }; //+------------------------------------------------------------------+ //| Average Directional Movement Index Wilder indicator class | //+------------------------------------------------------------------+ class CIndADXW : public CIndMSTF { public: //--- Constructor CIndADXW(const string symbol,const ENUM_TIMEFRAMES timeframe, const int adx_period // averaging period ) : CIndMSTF(IND_ADXW,3,symbol,timeframe) { // Номера буферов: 0 - MAIN_LINE, 1 - PLUSDI_LINE, 2 - MINUSDI_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(adx_period<1 ? 14 : adx_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),adx_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("ADX Wilder"); this.SetDescription("Average Directional Movement Index Wilder"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=2; //--- write descriptions of MAIN_LINE, PLUSDI_LINE and MINUSDI_LINE line buffers this.SetBufferDescription(MAIN_LINE,this.m_title); this.SetBufferDescription(PLUSDI_LINE,"+DI"); this.SetBufferDescription(MINUSDI_LINE,"-DI"); } }; //+------------------------------------------------------------------+ //| Alligator indicator class | //+------------------------------------------------------------------+ class CIndAlligator : public CIndMSTF { public: //--- Constructor CIndAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period, // period for calculating jaws const int jaw_shift, // horizontal shift of jaws const int teeth_period, // period for calculating teeth const int teeth_shift, // horizontal shift of teeth const int lips_period, // period for calculating lips const int lips_shift, // horizontal shift of lips const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_ALLIGATOR,3,symbol,timeframe) { // Buffer indexes: 0 - GATORJAW_LINE, 1 - GATORTEETH_LINE, 2 - GATORLIPS_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,8)==8) { ::ZeroMemory(this.m_param); //--- period for jaw line calculation this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period); //--- horizontal shift of the jaw line this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=jaw_shift; //--- period for teeth line calculation this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period); //--- horizontal shift of teeth line this.m_param[3].type=TYPE_INT; this.m_param[3].integer_value=teeth_shift; //--- period for lip line calculation this.m_param[4].type=TYPE_UINT; this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period); //--- horizontal shift of lips line this.m_param[5].type=TYPE_INT; this.m_param[5].integer_value=lips_shift; //--- smoothing type this.m_param[6].type=TYPE_UINT; this.m_param[6].integer_value=ma_method; //--- price type or handle this.m_param[7].type=TYPE_UINT; this.m_param[7].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("Alligator"); this.SetDescription("Alligator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; //--- Write descriptions of GATORJAW_LINE, GATORTEETH_LINE and GATORLIPS_LINE line buffers this.SetBufferDescription(GATORJAW_LINE,::StringFormat("Jaws(%s%lu)", (current ? "" : symbol_period+":"),jaw_period)); this.SetBufferDescription(GATORTEETH_LINE,::StringFormat("Teeth(%s%lu)",(current ? "" : symbol_period+":"),teeth_period)); this.SetBufferDescription(GATORLIPS_LINE,::StringFormat("Lips(%s%lu)", (current ? "" : symbol_period+":"),lips_period)); //--- Write offsets to buffers GATORJAW_LINE, GATORTEETH_LINE and GATORLIPS_LINE this.SetBufferShift(GATORJAW_LINE,jaw_shift); this.SetBufferShift(GATORTEETH_LINE,teeth_shift); this.SetBufferShift(GATORLIPS_LINE,lips_shift); } }; //+------------------------------------------------------------------+ //| Adaptive Moving Average indicator class | //+------------------------------------------------------------------+ class CIndAMA : public CIndMSTF { public: //--- Constructor CIndAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ama_period, // AMA period const int fast_ma_period, // fast MA period const int slow_ma_period, // slow MA period const int ama_shift, // horizontal shift of the indicator const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_AMA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,5)==5) { ::ZeroMemory(this.m_param); //--- AMA period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ama_period<1 ? 9 : ama_period); //--- fast MA period this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(fast_ma_period<1 ? 2 : fast_ma_period); //--- slow MA period this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(slow_ma_period<1 ? 30 : slow_ma_period); //--- horizontal shift of the indicator this.m_param[3].type=TYPE_INT; this.m_param[3].integer_value=ama_shift; //--- price type or handle this.m_param[4].type=TYPE_UINT; this.m_param[4].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),ama_period,fast_ma_period,slow_ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("AMA"); this.SetDescription("Adaptive Moving Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ama_shift); } }; //+------------------------------------------------------------------+ //| Awesome Oscillator indicator class | //+------------------------------------------------------------------+ class CIndAO : public CIndMSTF { public: //--- Constructor CIndAO(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_AO,1,symbol,timeframe) { //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("AO"); this.SetDescription("Awesome Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Average True Range indicator class | //+------------------------------------------------------------------+ class CIndATR : public CIndMSTF { public: //--- Constructor CIndATR(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period // averaging period ) : CIndMSTF(IND_ATR,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("ATR"); this.SetDescription("Average True Range"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Bears Power indicator class | //+------------------------------------------------------------------+ class CIndBears : public CIndMSTF { public: //--- Constructor CIndBears(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period // averaging period ) : CIndMSTF(IND_BEARS,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Bears"); this.SetDescription("Bears Power"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Bulls Power indicator class | //+------------------------------------------------------------------+ class CIndBulls : public CIndMSTF { public: //--- Constructor CIndBulls(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period // averaging period ) : CIndMSTF(IND_BULLS,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Bulls"); this.SetDescription("Bulls Power"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Bollinger Bands® indicator class | //+------------------------------------------------------------------+ class CIndBands : public CIndMSTF { public: //--- Constructor CIndBands(const string symbol,const ENUM_TIMEFRAMES timeframe, const int bands_period, // central line calculation period const int bands_shift, // horizontal shift of the indicator const double deviation, // number of standard deviations const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_BANDS,3,symbol,timeframe) { // Buffer indexes: 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- central line calculation period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(bands_period<1 ? 20 : bands_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=bands_shift; //--- number of standard deviations this.m_param[2].type=TYPE_DOUBLE; this.m_param[2].double_value=deviation; //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),bands_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Bands"); this.SetDescription("Bollinger Bands"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Description of line buffers BASE_LINE, UPPER_BAND and LOWER_BAND this.SetBufferDescription(BASE_LINE,this.m_title+" Middle"); this.SetBufferDescription(UPPER_BAND,this.m_title+" Upper"); this.SetBufferDescription(LOWER_BAND,this.m_title+" Lower"); //--- Write offsets to the BASE_LINE, UPPER_BAND and LOWER_BAND buffers this.SetBufferShift(BASE_LINE,bands_shift); this.SetBufferShift(UPPER_BAND,bands_shift); this.SetBufferShift(LOWER_BAND,bands_shift); } }; //+------------------------------------------------------------------+ //| Commodity Channel Index indicator class | //+------------------------------------------------------------------+ class CIndCCI : public CIndMSTF { public: //--- Constructor CIndCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_CCI,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period); //--- price type or handle this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("CCI"); this.SetDescription("Commodity Channel Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=2; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Chaikin Oscillator indicator class | //+------------------------------------------------------------------+ class CIndCHO : public CIndMSTF { public: //--- Constructor CIndCHO(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ma_period, // fast period const int slow_ma_period, // slow period const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_VOLUME applied_volume // used volume ) : CIndMSTF(IND_CHAIKIN,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- fast period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(fast_ma_period<1 ? 3 : fast_ma_period); //--- slow period this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(slow_ma_period<1 ? 10 : slow_ma_period); //--- smoothing type this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=ma_method; //--- used volume this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),slow_ma_period,fast_ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("CHO"); this.SetDescription("Chaikin Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=0; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Double Exponential Moving Average indicator class | //+------------------------------------------------------------------+ class CIndDEMA : public CIndMSTF { public: //--- Constructor CIndDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const int ma_shift, // horizontal indicator shift const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_DEMA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,3)==3) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- price type or handle this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("DEMA"); this.SetDescription("Double Exponential Moving Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| DeMarker indicator class | //+------------------------------------------------------------------+ class CIndDeM : public CIndMSTF { public: //--- Constructor CIndDeM(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period // averaging period ) : CIndMSTF(IND_DEMARKER,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("DeM"); this.SetDescription("DeMarker"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=3; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Envelopes indicator class | //+------------------------------------------------------------------+ class CIndEnvelopes : public CIndMSTF { public: //--- Constructor CIndEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // middle line calculation period const int ma_shift, // horizontal shift of the indicator const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_PRICE applied_price, // price type or handle const double deviation // deviation of envelope borders from the middle line ) : CIndMSTF(IND_ENVELOPES,2,symbol,timeframe) { // Buffer indexes: 0 - UPPER_LINE, 1 - LOWER_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,5)==5) { ::ZeroMemory(this.m_param); //--- central line calculation period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- smoothing type this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=ma_method; //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; //--- deviation of envelope borders from the muddle line this.m_param[4].type=TYPE_UINT; this.m_param[4].double_value=deviation; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Envelopes"); this.SetDescription("Envelopes"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Description of UPPER_LINE and LOWER_LINE line buffers this.SetBufferDescription(UPPER_LINE,this.m_title+" Upper"); this.SetBufferDescription(LOWER_LINE,this.m_title+" Lower"); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Force Index indicator class | //+------------------------------------------------------------------+ class CIndForce : public CIndMSTF { public: //--- Constructor CIndForce(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation ) : CIndMSTF(IND_FORCE,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,3)==3) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 13 : ma_period); //--- smoothing type this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=ma_method; //--- volume type for calculation this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Force"); this.SetDescription("Force Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Fractals indicator class | //+------------------------------------------------------------------+ class CIndFractals : public CIndMSTF { public: //--- Constructor CIndFractals(const string symbol,const ENUM_TIMEFRAMES timeframe) : CIndMSTF(IND_FRACTALS,2,symbol,timeframe) { // Buffer indexes: 0 - UPPER_LINE, 1 - LOWER_LINE //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("Fractals"); this.SetDescription("Fractals"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; //--- Description of UPPER_LINE and LOWER_LINE line buffers this.SetBufferDescription(UPPER_LINE,this.m_title+" Up"); this.SetBufferDescription(LOWER_LINE,this.m_title+" Down"); } }; //+------------------------------------------------------------------+ //| Fractal Adaptive Moving Average indicator class | //+------------------------------------------------------------------+ class CIndFrAMA : public CIndMSTF { public: //--- Constructor CIndFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const int ma_shift, // horizontal shift of the indicator const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_FRAMA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,3)==3) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- price type or handle this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("FRAMA"); this.SetDescription("Fractal Adaptive Moving Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Gator Oscillator indicator class | //+------------------------------------------------------------------+ class CIndGator : public CIndMSTF { public: //--- Constructor CIndGator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period, // period for jaw line calculation const int jaw_shift, // horizontal shift of jaw line const int teeth_period, // period for calculating teeth line const int teeth_shift, // horizontal shift of teeth line const int lips_period, // period for calculating lip line const int lips_shift, // horizontal shift of lip line const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_GATOR,4,symbol,timeframe) { // Buffer indexes: 0 - UPPER_HISTOGRAM, 1 - color buffer of the upper histogram, 2 - LOWER_HISTOGRAM, 3 - color buffer of the lower histogram //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,8)==8) { ::ZeroMemory(this.m_param); //--- period for jaw line calculation this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(jaw_period<1 ? 13 : jaw_period); //--- horizontal shift of the jaw line this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=jaw_shift; //--- period for teeth line calculation this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(teeth_period<1 ? 8 : teeth_period); //--- horizontal shift of teeth line this.m_param[3].type=TYPE_INT; this.m_param[3].integer_value=teeth_shift; //--- period for lip line calculation this.m_param[4].type=TYPE_UINT; this.m_param[4].integer_value=(lips_period<1 ? 5 : lips_period); //--- horizontal shift of lips line this.m_param[5].type=TYPE_INT; this.m_param[5].integer_value=lips_shift; //--- smoothing type this.m_param[6].type=TYPE_UINT; this.m_param[6].integer_value=ma_method; //--- price type or handle this.m_param[7].type=TYPE_UINT; this.m_param[7].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),jaw_period,teeth_period,lips_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Gator"); this.SetDescription("Gator Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; this.m_digits=::Digits()+1; //--- Description of line buffers UPPER_HISTOGRAM, upper histogram color buffer, LOWER_HISTOGRAM and lower histogram color buffer this.SetBufferDescription(UPPER_HISTOGRAM,this.m_title+" Up"); this.SetBufferDescription(1,this.m_title+" Colors Up"); this.SetBufferDescription(LOWER_HISTOGRAM,this.m_title+" Down"); this.SetBufferDescription(3,this.m_title+" Colors Down"); //--- Записываем смещения в буферы UPPER_HISTOGRAM, 1, LOWER_HISTOGRAM и 2 this.SetBufferShift(UPPER_HISTOGRAM,teeth_shift); this.SetBufferShift(1,teeth_shift); this.SetBufferShift(LOWER_HISTOGRAM,lips_shift); this.SetBufferShift(3,lips_shift); } }; //+------------------------------------------------------------------+ //| Ichimoku Kinko Hyo indicator class | //+------------------------------------------------------------------+ class CIndIchimoku : public CIndMSTF { public: //--- Constructor CIndIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe, const int tenkan_sen, // period of Tenkan-sen const int kijun_sen, // period of Kijun-sen const int senkou_span_b // period of Senkou Span B ) : CIndMSTF(IND_ICHIMOKU,5,symbol,timeframe) { // Buffer indexes: 0 - TENKANSEN_LINE, 1 - KIJUNSEN_LINE, 2 - SENKOUSPANA_LINE, 3 - SENKOUSPANB_LINE, 4 - CHIKOUSPAN_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,3)==3) { ::ZeroMemory(this.m_param); //--- period of Tenkan-sen this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(tenkan_sen<1 ? 9 : tenkan_sen); //--- period of Kijun-sen this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(kijun_sen<1 ? 26 : kijun_sen); //--- period of Senkou Span B this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(senkou_span_b<1 ? 52 : senkou_span_b); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),tenkan_sen,kijun_sen,senkou_span_b); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Ichimoku"); this.SetDescription("Ichimoku Kinko Hyo"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Description of line buffers TENKANSEN_LINE, KIJUNSEN_LINE, SENKOUSPANA_LINE, SENKOUSPANB_LINE and CHIKOUSPAN_LINE this.SetBufferDescription(TENKANSEN_LINE,::StringFormat("Tenkan-sen(%lu)",tenkan_sen)); this.SetBufferDescription(KIJUNSEN_LINE,::StringFormat("Kijun-sen(%lu)",kijun_sen)); this.SetBufferDescription(SENKOUSPANA_LINE,"Senkou Span A"); this.SetBufferDescription(SENKOUSPANB_LINE,::StringFormat("Senkou Span B(%lu)",senkou_span_b)); this.SetBufferDescription(CHIKOUSPAN_LINE,"Chikou Span"); //--- Write shifts to buffers SENKOUSPANA_LINE, SENKOUSPANB_LINE and CHIKOUSPAN_LINE //this.SetBufferShift(SENKOUSPANA_LINE,kijun_sen); //this.SetBufferShift(SENKOUSPANB_LINE,kijun_sen); //this.SetBufferShift(CHIKOUSPAN_LINE,kijun_sen-senkou_span_b); } }; //+------------------------------------------------------------------+ //| Market Facilitation Index indicator class | //+------------------------------------------------------------------+ class CIndBWMFI : public CIndMSTF { public: //--- Constructor CIndBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation ) : CIndMSTF(IND_BWMFI,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- volume type for calculation this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("BW MFI"); this.SetDescription("Market Facilitation Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_WILLIAMS; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Momentum indicator class | //+------------------------------------------------------------------+ class CIndMomentum : public CIndMSTF { public: //--- Constructor CIndMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe, const int mom_period, // averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_MOMENTUM,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(mom_period<1 ? 14 : mom_period); //--- price type or handle this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),mom_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Momentum"); this.SetDescription("Momentum"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=2; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Money Flow Index indicator class | //+------------------------------------------------------------------+ class CIndMFI : public CIndMSTF { public: //--- Constructor CIndMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation ) : CIndMSTF(IND_MFI,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); //--- volume type for calculation this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("MFI"); this.SetDescription("Money Flow Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_VOLUME; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Moving Average indicator class | //+------------------------------------------------------------------+ class CIndMA : public CIndMSTF { public: //--- Constructor CIndMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const int ma_shift, // horizontal shift of the indicator const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_MA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- smoothing type this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=ma_method; //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("MA"); this.SetDescription("Moving Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Moving Average of Oscillator indicator class | //+------------------------------------------------------------------+ class CIndOsMA : public CIndMSTF { public: //--- Constructor CIndOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, // fast MA period const int slow_ema_period, // slow MA period const int signal_period, // difference averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_OSMA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- fast MA period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period); //--- slow MA period this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period); //--- difference averaging period this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period<2 ? 2 : signal_period); //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("OsMA"); this.SetDescription("Moving Average of Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=::Digits()+2; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Moving Averages Convergence/Divergence indicator class | //+------------------------------------------------------------------+ class CIndMACD : public CIndMSTF { public: //--- Constructor CIndMACD(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period, // fast MA period const int slow_ema_period, // slow MA period const int signal_period, // difference averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_MACD,2,symbol,timeframe) { // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- fast MA period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(fast_ema_period<1 ? 12 : fast_ema_period); //--- slow MA period this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(slow_ema_period<1 ? 26 : slow_ema_period); //--- difference averaging period this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(signal_period<1 ? 9 : signal_period); //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),fast_ema_period,slow_ema_period,signal_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("MACD"); this.SetDescription("Moving Averages Convergence/Divergence"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=::Digits()+1; //--- Description of MAIN_LINE and SIGNAL_LINE line buffers this.SetBufferDescription(MAIN_LINE,this.m_title); this.SetBufferDescription(SIGNAL_LINE,"Signal"); } }; //+------------------------------------------------------------------+ //| On Balance Volume indicator class | //+------------------------------------------------------------------+ class CIndOBV : public CIndMSTF { public: //--- Constructor CIndOBV(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume // volume type for calculation ) : CIndMSTF(IND_OBV,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- volume type for calculation this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("OBV"); this.SetDescription("On Balance Volume"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_VOLUME; this.m_digits=0; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Parabolic Stop and Reverse system indicator class | //+------------------------------------------------------------------+ class CIndSAR : public CIndMSTF { public: //--- Constructor CIndSAR(const string symbol,const ENUM_TIMEFRAMES timeframe, const double step, // price change step — acceleration factor const double maximum // maximum step ) : CIndMSTF(IND_SAR,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- price change step — acceleration factor this.m_param[0].type=TYPE_DOUBLE; this.m_param[0].double_value=step; //--- maximum step this.m_param[1].type=TYPE_DOUBLE; this.m_param[1].double_value=maximum; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%.2f,%.2f)",symbol_period,(current ? "" : ":"),step,maximum); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("SAR"); this.SetDescription("Parabolic SAR"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Класс индикатора Relative Strength Index | //+------------------------------------------------------------------+ class CIndRSI : public CIndMSTF { public: //--- Constructor CIndRSI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_RSI,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period); //--- price type or handle this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("RSI"); this.SetDescription("Relative Strength Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=2; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Relative Vigor Index indicator class | //+------------------------------------------------------------------+ class CIndRVI : public CIndMSTF { public: //--- Constructor CIndRVI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period // averaging period ) : CIndMSTF(IND_RVI,2,symbol,timeframe) { // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 10 : ma_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("RVI"); this.SetDescription("Relative Vigor Index"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=3; //--- Description of MAIN_LINE and SIGNAL_LINE line buffers this.SetBufferDescription(MAIN_LINE,this.m_title); this.SetBufferDescription(SIGNAL_LINE,"Signal"); } }; //+------------------------------------------------------------------+ //| Standard Deviation indicator class | //+------------------------------------------------------------------+ class CIndStdDev : public CIndMSTF { public: //--- Constructor CIndStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const int ma_shift, // horizontal shift of the indicator const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_STDDEV,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 20 : ma_period<2 ? 2 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- smoothing type this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=ma_method; //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("StdDev"); this.SetDescription("Standard Deviation"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Stochastic Oscillator indicator class | //+------------------------------------------------------------------+ class CIndStoch : public CIndMSTF { public: //--- Constructor CIndStoch(const string symbol,const ENUM_TIMEFRAMES timeframe, const int Kperiod, // K-period (number of bars for calculations) const int Dperiod, // D-period (primary smoothing period) const int slowing, // final smoothing const ENUM_MA_METHOD ma_method, // smoothing type const ENUM_STO_PRICE price_field // Stochastic calculation method ) : CIndMSTF(IND_STOCHASTIC,2,symbol,timeframe) { // Buffer indexes: 0 - MAIN_LINE, 1 - SIGNAL_LINE //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,5)==5) { ::ZeroMemory(this.m_param); //--- K period (number of bars for calculation) this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(Kperiod<1 ? 5 : Kperiod); //--- D period (primary smoothing period) this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(Dperiod<1 ? 3 : Dperiod); //--- final smoothing this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=(slowing<1 ? 3 : slowing); //--- smoothing type this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=ma_method; //--- Stochastic calculation method this.m_param[4].type=TYPE_UINT; this.m_param[4].integer_value=price_field; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu,%lu)",symbol_period,(current ? "" : ":"),Kperiod,Dperiod,slowing); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Stoch"); this.SetDescription("Stochastic Oscillator"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=2; //--- Description of MAIN_LINE and SIGNAL_LINE line buffers this.SetBufferDescription(MAIN_LINE,this.m_title); this.SetBufferDescription(SIGNAL_LINE,"Signal"); } }; //+------------------------------------------------------------------+ //| Triple Exponential Moving Average indicator class | //+------------------------------------------------------------------+ class CIndTEMA : public CIndMSTF { public: //--- Constructor CIndTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const int ma_shift, // horizontal shift of the indicator const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_TEMA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,3)==3) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period); //--- horizontal shift of the indicator this.m_param[1].type=TYPE_INT; this.m_param[1].integer_value=ma_shift; //--- price type or handle this.m_param[2].type=TYPE_UINT; this.m_param[2].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("TEMA"); this.SetDescription("Triple Exponential Moving Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Triple Exponential Moving Averages Oscillator indicator class | //+------------------------------------------------------------------+ class CIndTriX : public CIndMSTF { public: //--- Constructor CIndTriX(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, // averaging period const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_TRIX,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,2)==2) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(ma_period<1 ? 14 : ma_period<2 ? 2 : ma_period); //--- price type or handle this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),ma_period); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName("TRIX"); this.SetDescription("Triple Exponential Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Larry Williams' Percent Range indicator class | //+------------------------------------------------------------------+ class CIndWPR : public CIndMSTF { public: //--- Constructor CIndWPR(const string symbol,const ENUM_TIMEFRAMES timeframe, const int calc_period // averaging period ) : CIndMSTF(IND_WPR,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- averaging period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(calc_period<1 ? 14 : calc_period); } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu)",symbol_period,(current ? "" : ":"),calc_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("%R"); this.SetDescription("Williams' Percent Range"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_OSCILLATOR; this.m_digits=2; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Variable Index Dynamic Average indicator class | //+------------------------------------------------------------------+ class CIndVIDyA : public CIndMSTF { public: //--- Constructor CIndVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int cmo_period, // the Chande Momentum period const int ema_period, // period of the smoothing factor const int ma_shift, // horizontal shift of the indicator const ENUM_APPLIED_PRICE applied_price // price type or handle ) : CIndMSTF(IND_VIDYA,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,4)==4) { ::ZeroMemory(this.m_param); //--- Chande Momentum period this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=(cmo_period<1 ? 9 : cmo_period); //--- smoothing factor period this.m_param[1].type=TYPE_UINT; this.m_param[1].integer_value=(ema_period<1 ? 12 : ema_period); //--- horizontal shift of the indicator this.m_param[2].type=TYPE_INT; this.m_param[2].integer_value=ma_shift; //--- price type or handle this.m_param[3].type=TYPE_UINT; this.m_param[3].integer_value=applied_price; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=StringFormat("(%s%s%lu,%lu)",symbol_period,(current ? "" : ":"),cmo_period,ema_period); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("VIDYA"); this.SetDescription("Variable Index Dynamic Average"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_TREND; this.m_digits=::Digits()+1; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); //--- Write shift to buffer 0 this.SetBufferShift(0,ma_shift); } }; //+------------------------------------------------------------------+ //| Volumes indicator class | //+------------------------------------------------------------------+ class CIndVolumes : public CIndMSTF { public: //--- Constructor CIndVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe, const ENUM_APPLIED_VOLUME applied_volume // volume type ) : CIndMSTF(IND_VOLUMES,1,symbol,timeframe) { //--- Set the size of the parameter array and fill it ::ResetLastError(); if(::ArrayResize(this.m_param,1)==1) { ::ZeroMemory(this.m_param); //--- volume type this.m_param[0].type=TYPE_UINT; this.m_param[0].integer_value=applied_volume; } else ::PrintFormat("%s: ArrayResize failed. Error %ld",__FUNCTION__,::GetLastError()); //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title, category and Digits this.SetParameters(param); this.SetName("Volumes"); this.SetDescription("Volumes"); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_VOLUME; this.m_digits=0; //--- Write description of line buffers this.SetBufferDescription(0,this.m_title); } }; //+------------------------------------------------------------------+ //| Custom indicator class | //+------------------------------------------------------------------+ class CIndCustom : public CIndMSTF { public: //--- Constructor CIndCustom(const string symbol,const ENUM_TIMEFRAMES timeframe, const string path, // path to the indicator (for example, "Examples\\MACD.ex5") const string name, // name of the custom indicator const uint buffers, // number of indicator buffers const MqlParam ¶m[] // array of custom indicator parameters ) : CIndMSTF(IND_CUSTOM,buffers,symbol,timeframe) { //--- If an empty array of parameters is passed, print this to log int total=(int)param.Size(); if(total==0) ::PrintFormat("%s Error. Passed an empty array",__FUNCTION__); //--- If the array is not empty and its size is increased by 1 (the first string parameter must contain the indicator name) ResetLastError(); if(total>0 && ::ArrayResize(this.m_param,total+1)==total+1) { //--- Reset data in the array and enter name (path to file and name of .ex5 file) ::ZeroMemory(this.m_param); //--- name of the custom indicator this.m_param[0].type=TYPE_STRING; this.m_param[0].string_value=path; //--- fill the array of indicator parameters for(int i=0;i<total;i++) { this.m_param[i+1].type=param[i].type; this.m_param[i+1].double_value=param[i].double_value; this.m_param[i+1].integer_value=param[i].integer_value; this.m_param[i+1].string_value=param[i].string_value; } //--- Create description of parameters //--- If non-current chart symbol or period, their descriptions are added to parameters bool current=(this.Symbol()==::Symbol() && this.Timeframe()==::Period()); string symbol_period=(current ? "" : ::StringFormat("%s,%s",this.Symbol(),this.TimeframeDescription())); string param=(current ? "" : StringFormat("(%s)",symbol_period)); //--- Write description of parameters, indicator name, its description, title and category this.SetParameters(param); this.SetName(name); this.SetDescription(name); this.m_title=this.Name()+this.Parameters(); this.m_category=IND_CATEGORY_CUSTOM; //--- Write a description of the first line buffer this.SetBufferDescription(0,this.m_title); } } };
Die vorgestellte Liste enthält alle Klassen zur Erstellung aller im Client-Terminal verfügbaren technischen Indikatoren und zusätzlich eine Klasse zur Erstellung eines nutzerdefinierten Indikators. Alle Klassen sind identisch und ihre wichtigsten Punkte sind im Klassencode kommentiert.
Bitte beachten Sie, dass die Tests die Erstellung und den Betrieb einiger der oben genannten Indikatoren umfassten. Es wurden nämlich nur Indikatoren getestet, die auf gleitenden Durchschnitten basieren und eine Linie auf dem Hauptdiagramm darstellen. In den nächsten Artikeln werden wir Vorlagen für die Erstellung von Multi-Symbol- und Multi-Perioden-Versionen aller Arten von technischen Indikatoren erstellen.
In der Tat ist bereits alles für die Erstellung von Multi-Indikatoren vorbereitet. Wir werden jedoch über diese Idee hinausgehen und ein praktisches Tool zur Automatisierung der Erstellung und Verwendung von Multi-Symbol- und Multi-Perioden-Indikatoren entwickeln. Dabei handelt es sich um eine Sammelklasse von Indikatoren, die es Ihnen ermöglicht, auf einfache Weise beliebige Indikatoren auf der Grundlage von Standard- oder nutzerdefinierten Indikatoren zu erstellen und sie in Multi-Symbol- und Multi-Perioden-Indikatoren zu verwandeln.
Klasse der Indikator-Kollektion
Die Klasse der Indikatorkollektion ist im Wesentlichen eine reguläre Liste von Zeigern auf Objekte. Zu dieser Liste werden wir Methoden hinzufügen, mit denen wir bequem Indikatoren erstellen und neu erstellte Indikatoren zur Kollektion hinzufügen können. Es wird auch möglich sein, auf einfache Weise einen Zeiger auf den gewünschten Indikator zu erhalten und die erforderlichen Daten von ihm zu verwenden. Wenn ein neuer Indikator zu einer Kollektion hinzugefügt wird, wird zunächst geprüft, ob genau der gleiche Indikator in der Kollektion vorhanden ist. Wenn derselbe Indikator bereits in der Kollektion vorhanden ist, wird das neu erstellte Objekt gelöscht und ein Zeiger auf das in der Kollektion vorhandene Objekt zurückgegeben. Dadurch wird vermieden, dass sich zwei verschiedene Objekte auf denselben Berechnungsteil beziehen.
Die Struktur der Sammelklasse entspricht der Struktur der grundlegenden Multi-Symbol-Multi-Perioden-Indikatorenklasse. Der einzige Unterschied besteht darin, dass die meisten Methoden zunächst den gewünschten Indikator anhand seines Handles finden und dann die entsprechende Methode aufrufen müssen, um seinen Wert oder sein Aktionsergebnis zu setzen oder zu erhalten.
Wir werden die gleiche Datei \MQL5\Include\IndMSTF\IndMSTF.mqh verwenden und mit dem Hinzufügen einer neuen Klasse fortfahren. Für das Funktionieren muss die Datei der Klasse mit dem dynamischen Array von Zeigern auf Instanzen der Klasse CObject und ihrer abgeleiteten Klassen CArrayObj eingebunden werden:
//+------------------------------------------------------------------+ //| Indicator collection class | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> class CMSTFIndicators { private: public: }
Das Listenobjekt und die Methoden für die Arbeit mit der Klasse wird in den Klassenkörper eingefügt:
//+------------------------------------------------------------------+ //| Indicator collection class | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> class CMSTFIndicators { private: CArrayObj m_list; //--- Creates an indicator for the passed object bool CreateIndicator(CIndMSTF *ind_obj); //--- Adds the specified indicator to the collection int AddNewIndicator(CIndMSTF *ind_obj,const string source); public: //--- Returns (1) indicator object by handle, (2) number of indicators in the collection CIndMSTF *GetIndicatorObj(const int ind_handle,const string source) const; uint IndicatorsTotal(void) const { return this.m_list.Total(); } //--- Populates buffers of (1) the indicator by handle, (2) all indicators in the collection bool Calculate(const int ind_handle); bool Calculate(void); //--- Sets the (1) specified, (2) default description of the indicator buffer line void SetPlotLabel(const uint plot_index,const string descript); void SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num); //--- Sets the shift to the specified plotting buffer void SetPlotShift(const uint plot_index,const int shift); //--- (1) Sets (2) returns the initializing value of the given buffer specified by the indicator handle void SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value); double BufferInitValue(const int ind_handle,const uint buffer_num) const; //--- Returns indicator data by handle from the specified buffer at index (1) as is, (2) for the specified symbol/timeframe double GetData(const int ind_handle,const uint buffer_num,const uint index); double GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index); //--- (1) Copies data of the specified calculation part buffer of the indicator by handle into the indicator buffer, taking into account chart symbol/period, //--- (2) returns the amount of data in the specified buffer of the indicator by handle bool DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const int limit,double &buffer[]); uint DataTotal(const int ind_handle,const uint buffer_num) const; //--- Returns (1) buffer description, (2) state of the line data of given buffer of indicator specified by handle on the specified bar //--- (3) indicator line state for the specific chart symbol/period, (4) indicator line state relation to the specified level, //--- (5) state of relation of indicator line with specified level for certain chart symbol/period, (6) indicator category description string BufferDescription(const int ind_handle,const uint buffer_num); ENUM_LINE_STATE BufferLineState(const int ind_handle,const uint buffer_num,const int index); ENUM_LINE_STATE BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const int index); ENUM_LINE_STATE BufferLineStateRelative(const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE); ENUM_LINE_STATE BufferLineStateRelative(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE); string CategoryDescription(const int ind_handle); //--- Sets (1) identifier, (2) Digits, (3) user description, (4) buffer description void SetID(const int ind_handle,const int id); void SetDigits(const int ind_handle,const int digits); void SetDescription(const int ind_handle,const string descr); void SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr); //--- Returns flag of whether the buffer is set as series, (2) historical data for symbol/period is synchronized bool IsSeries(const int ind_handle,const uint buffer_num) const; bool IsSynchronized(const int ind_handle) const; //--- Returns (1) timeframe, (2) symbol, (3) name, (4) list of parameters, (5) handle, (6) Digits //--- number of (7) buffers, (8) bars, (9) identifier, (10) description, (11) title, (12) category, //--- (13) number of parameters, (14) program type, description of (15) category, (16) indicator buffer ENUM_TIMEFRAMES Timeframe(const int ind_handle) const; string Symbol(const int ind_handle) const; string Name(const int ind_handle) const; string Parameters(const int ind_handle) const; int Digits(const int ind_handle) const; uint BuffersTotal(const int ind_handle) const; uint RatesTotal(const int ind_handle) const; int ID(const int ind_handle) const; string Description(const int ind_handle) const; string Title(const int ind_handle) const; ENUM_IND_CATEGORY Category(const int ind_handle) const; uint ParamsTotal(const int ind_handle) const; //--- Returns (1) structure of parameters by index from array, (2) timeframe description MqlParam GetMqlParam(const int ind_handle,const int index) const; string TimeframeDescription(const int ind_handle) const; //--- Returns amount of calculated data int Calculated(const int ind_handle) const; //--- Virtual method returning the type of object (indicator) ENUM_INDICATOR Type(const int ind_handle) const; //--- Methods for adding indicators to the collection int AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe); int AddNewAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14); int AddNewADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14); int AddNewAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN); int AddNewAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ama_period=9, const int fast_ma_period=2, const int slow_ma_period=30, const int ama_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewAO(const string symbol,const ENUM_TIMEFRAMES timeframe); int AddNewATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14); int AddNewBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13); int AddNewBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13); int AddNewBands(const string symbol,const ENUM_TIMEFRAMES timeframe,const int bands_period=20, const int bands_shift=0, const double deviation=2.0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL); int AddNewChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ma_period=3, const int slow_ma_period=10, const ENUM_MA_METHOD ma_method=MODE_EMA, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14); int AddNewEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE, const double deviation=0.1); int AddNewForce(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewFractals(const string symbol,const ENUM_TIMEFRAMES timeframe); int AddNewFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewGator(const string symbol,const ENUM_TIMEFRAMES timeframe,const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN); int AddNewIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe,const int tenkan_sen=9, const int kijun_sen=26, const int senkou_span_b=52); int AddNewBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe,const int mom_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ema_period=12, const int slow_ema_period=26, const int signal_period=9, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewMACD(const string symbol,const ENUM_TIMEFRAMES timeframe,const int fast_ema_period=12, const int slow_ema_period=26, const int signal_period=9, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewSAR(const string symbol,const ENUM_TIMEFRAMES timeframe,const double step=0.02, const double maximum=0.2); int AddNewRSI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10); int AddNewStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=20, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe,const int Kperiod=5, const int Dperiod=3, const int slowing=3, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_STO_PRICE price_field=STO_LOWHIGH); int AddNewTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const int ma_shif=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewTriX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period=14); int AddNewVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe,const int cmo_period=9, const int ema_period=12, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE); int AddNewVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK); int AddNewCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const string path, // path to the indicator (for example, "Examples\\MACD.ex5") const string name, // name of custom indicator (for example, "Custom MACD") const uint buffers, // number of buffers const MqlParam ¶m[]);// array of parameters //--- Timer void OnTimer(void) { //--- In a loop through all indicators form the collection int total=this.m_list.Total(); for(int i=0;i<total;i++) { //--- get a pointer to the next indicator object //--- and call its timer CIndMSTF *obj=this.m_list.At(i); if(obj!=NULL) obj.OnTimer(); } } //--- Constructor/destructor CMSTFIndicators(void){ this.m_list.Clear(); } ~CMSTFIndicators(void){;} };
Die Einführung von Methoden für die Arbeit mit den Indikatoren der Liste:
//+------------------------------------------------------------------+ //| Creates indicator calculation part for the passed object | //+------------------------------------------------------------------+ bool CMSTFIndicators::CreateIndicator(CIndMSTF *ind_obj) { //--- If the calculation part of the indicator could not be created if(!ind_obj.CreateIndicator()) { //--- look for the index of the indicator object in the collection list //--- using the index, delete the indicator object from the collection list this.m_list.Sort(); int index=this.m_list.Search(ind_obj); this.m_list.Delete(index); //--- Return false return false; } //--- The calculation part has been successfully created - return true return true; } //+------------------------------------------------------------------+ //| Returns indicator object by the calculation part handle | //+------------------------------------------------------------------+ CIndMSTF *CMSTFIndicators::GetIndicatorObj(const int ind_handle,const string source) const { //--- If an invalid handle is passed to the method, report this and return NULL if(ind_handle==INVALID_HANDLE) { ::PrintFormat("%s: Error handle",source); return NULL; } //--- In a loop through all indicator objects in the collection list int total=this.m_list.Total(); for(int i=0;i<total;i++) { //--- get a pointer to the next indicator object CIndMSTF *obj=this.m_list.At(i); if(obj==NULL) continue; //--- If the indicator handle is equal to that passed to the method - //--- return a pointer to the found indicator object if(obj.Handle()==ind_handle) return obj; } //--- Nothing is found - return NULL return NULL; } //+------------------------------------------------------------------+ //| Populate buffers of the indicator at the handle | //+------------------------------------------------------------------+ bool CMSTFIndicators::Calculate(const int ind_handle) { //--- Get a pointer to an indicator object using the handle CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) return false; //--- Return the result of the Calculate method obtained from the handle of the indicator object return obj.Calculate(); } //+------------------------------------------------------------------+ //| Populate buffers of all indicators in the collection | //+------------------------------------------------------------------+ bool CMSTFIndicators::Calculate(void) { //--- Declare the variable for storing the result bool res=true; //--- In a loop through all indicator objects in the collection list int total=this.m_list.Total(); for(int i=0;i<total;i++) { //--- get a pointer to the next indicator object CIndMSTF *obj=this.m_list.At(i); if(obj==NULL) continue; //--- Add to the 'res' variable the result of calling the Calculate method of the next indicator object res &=obj.Calculate(); //--- If the method worked with an error, inform that in the journal if(!res) ::PrintFormat("%s::%s: Error in indicator calculation: %s",__FUNCTION__,obj.Title(),TypeErrorcDescription(obj.TypeError())); } //--- If the overall result is false, inform of that in the journal if(!res) ::PrintFormat("%s: Not all indicators have been calculated successfully. It is necessary to recalculate the buffers of all indicators",__FUNCTION__); //--- Return the result of calling the Calculate methods of all indicators in the collection return res; } //+------------------------------------------------------------------+ //| Returns data of the indicator at the handle | //| from the specified buffers by index as is | //+------------------------------------------------------------------+ double CMSTFIndicators::GetData(const int ind_handle,const uint buffer_num,const uint index) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return EMPTY_VALUE; } //--- Return data from the specified indicator buffer at the index passed to the method return obj.GetData(buffer_num,index); } //+------------------------------------------------------------------+ //| Returns data of the indicator at the handle | //| from the specified buffer at index for this symbol/timeframe | //+------------------------------------------------------------------+ double CMSTFIndicators::GetDataTo(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const uint index) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return EMPTY_VALUE; } //--- Return data from the specified indicator buffer at the index passed to the method return obj.GetDataTo(symbol_to,timeframe_to,buffer_num,index); } //+------------------------------------------------------------------+ //| Fills the passed indicator buffer with data | //+------------------------------------------------------------------+ bool CMSTFIndicators::DataToBuffer(const string symbol_to,const ENUM_TIMEFRAMES timeframe_to,const int ind_handle,const uint buffer_num,const int limit,double &buffer[]) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return false; } //--- Fill the buffer array passed to the method from the specified indicator buffer return obj.DataToBuffer(symbol_to,timeframe_to,buffer_num,limit,buffer); } //+------------------------------------------------------------------+ //| Set the specified description for the buffer line | //+------------------------------------------------------------------+ void CMSTFIndicators::SetPlotLabel(const uint plot_index,const string descript) { ::PlotIndexSetString(plot_index,PLOT_LABEL,descript); } //+------------------------------------------------------------------+ //| Set default description for the buffer line | //+------------------------------------------------------------------+ void CMSTFIndicators::SetPlotLabelFromBuffer(const uint plot_index,const int ind_handle,const uint buffer_num) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set the description of the specified indicator buffer to the specified plotting buffer ::PlotIndexSetString(plot_index,PLOT_LABEL,obj.BufferDescription(buffer_num)); } //+------------------------------------------------------------------+ //| Set the shift for the specified plotting buffer | //+------------------------------------------------------------------+ void CMSTFIndicators::SetPlotShift(const uint plot_index,const int shift) { ::PlotIndexSetInteger(plot_index,PLOT_SHIFT,shift); } //+------------------------------------------------------------------+ //| Return the description of the given buffer | //| of the indicator specified by handle | //+------------------------------------------------------------------+ string CMSTFIndicators::BufferDescription(const int ind_handle,const uint buffer_num) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If pointer to object received, return description of the specified buffer from it. Otherwise error text return(obj!=NULL ? obj.BufferDescription(buffer_num) : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Set the initializing value for the specified buffer | //| of the indicator specified by handle | //+------------------------------------------------------------------+ void CMSTFIndicators::SetBufferInitValue(const int ind_handle,const uint buffer_num,const double value) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set the specified initializing "empty" value for the specified buffer obj.SetBufferInitValue(buffer_num,value); } //+------------------------------------------------------------------+ //| Return the initialization value of the specified buffer | //| of the indicator specified by handle | //+------------------------------------------------------------------+ double CMSTFIndicators::BufferInitValue(const int ind_handle,const uint buffer_num) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return WRONG_VALUE; } //--- Return the initializing "empty" value set for the specified buffer return obj.BufferInitValue(buffer_num); } //+------------------------------------------------------------------+ //| Returns the line data state of the given buffer | //| specified by indicator handle at the specified bar | //+------------------------------------------------------------------+ ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const int ind_handle,const uint buffer_num,const int index) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return LINE_STATE_NONE; } //--- Return the state of the line of the specified buffer at the specified index return obj.BufferLineState(buffer_num,index); } //+------------------------------------------------------------------+ //| Returns the line data state of the given buffer | //| specified by indicator handle at the symbol/timeframe bar | //+------------------------------------------------------------------+ ENUM_LINE_STATE CMSTFIndicators::BufferLineState(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const uint buffer_num,const int index) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return LINE_STATE_NONE; } //--- Get the time of the passed to the method datetime array[1]; if(::CopyTime(symbol,timeframe,index,1,array)!=1) { ::PrintFormat("%s::%s: Failed to get the time of the bar with index %ld. Error %lu",__FUNCTION__,obj.Title(),index,::GetLastError()); return LINE_STATE_NONE; } //--- Get the bar index in the indicator object buffer corresponding to the found time int bar=::iBarShift(obj.Symbol(),obj.Timeframe(),array[0]); //--- If a bar is found, return the line state on the found bar, otherwise an undefined state return(bar!=WRONG_VALUE ? obj.BufferLineState(buffer_num,bar) : LINE_STATE_NONE); } //+------------------------------------------------------------------+ //| Return the line data ratio for the given buffer | //| specified by indicator handle at the specified bar | //| with specified valiues | //+------------------------------------------------------------------+ ENUM_LINE_STATE CMSTFIndicators::BufferLineStateRelative(const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return LINE_STATE_NONE; } //--- Return the ratio of the indicator line and the level in the specified buffer at the specified index return obj.BufferLineStateRelative(buffer_num,index,level0,level1); } //+------------------------------------------------------------------+ //| Return the line data ratio for the given buffer | //| specified by indicator handle at the specified bar | //| with the specified values on the specified chart symbol/period | //+------------------------------------------------------------------+ ENUM_LINE_STATE CMSTFIndicators::BufferLineStateRelative(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ind_handle,const int buffer_num,const int index,const double level0,const double level1=EMPTY_VALUE) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return LINE_STATE_NONE; } //--- Return the ratio of the indicator line and the level in the specified buffer at the specified index return obj.BufferLineStateRelative(symbol,timeframe,buffer_num,index,level0,level1); } //+------------------------------------------------------------------+ //| Return category description | //+------------------------------------------------------------------+ string CMSTFIndicators::CategoryDescription(const int ind_handle) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return the description of the category Otherwise error text return(obj!=NULL ? obj.CategoryDescription() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Set identifier | //+------------------------------------------------------------------+ void CMSTFIndicators::SetID(const int ind_handle,const int id) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set the identifier for the received object obj.SetID(id); } //+------------------------------------------------------------------+ //| Set Digits of the indicator | //+------------------------------------------------------------------+ void CMSTFIndicators::SetDigits(const int ind_handle,const int digits) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set Digits for the received object obj.SetDigits(digits); } //+------------------------------------------------------------------+ //| Set a custom description | //+------------------------------------------------------------------+ void CMSTFIndicators::SetDescription(const int ind_handle,const string descr) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set a description for the received object obj.SetDescription(descr); } //+------------------------------------------------------------------+ //| Set a description of the specified buffer | //+------------------------------------------------------------------+ void CMSTFIndicators::SetBufferDescription(const int ind_handle,const uint buffer_num,const string descr) { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return; } //--- Set a description for the specified buffer of the received object obj.SetBufferDescription(buffer_num,descr); } //+------------------------------------------------------------------+ //| Returns the timeseries flag of the given buffer | //+------------------------------------------------------------------+ bool CMSTFIndicators::IsSeries(const int ind_handle,const uint buffer_num) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return false; } //--- Return the timeseries flag of the specified buffer of the received object return obj.IsSeries(buffer_num); } //+------------------------------------------------------------------+ //| Returns the synchronization flag for | //| historical data for the symbol/period | //+------------------------------------------------------------------+ bool CMSTFIndicators::IsSynchronized(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return false; } //--- Return the synchronization flag of the received object return obj.IsSynchronized(); } //+------------------------------------------------------------------+ //| Return the timeframe of the specified indicator | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CMSTFIndicators::Timeframe(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return WRONG_VALUE; } //--- Return the timeframe of the received object return obj.Timeframe(); } //+------------------------------------------------------------------+ //| Returns the symbol of the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::Symbol(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return the name of the symbol Otherwise error text return(obj!=NULL ? obj.Symbol() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Return the name of the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::Name(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return the name of the indicator Otherwise error text return(obj!=NULL ? obj.Name() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Returns a list of parameters of the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::Parameters(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return a list of indicator parameters Otherwise error text return(obj!=NULL ? obj.Parameters() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Return Digits of the specified indicator | //+------------------------------------------------------------------+ int CMSTFIndicators::Digits(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return WRONG_VALUE; } //--- Return Digits of the received object return obj.Digits(); } //+------------------------------------------------------------------+ //| Return the number of buffers of the specified indicator | //+------------------------------------------------------------------+ uint CMSTFIndicators::BuffersTotal(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return 0; } //--- Return the number of buffers of the received object return obj.BuffersTotal(); } //+------------------------------------------------------------------+ //| Return the number of timeseries bars for specified the indicator | //+------------------------------------------------------------------+ uint CMSTFIndicators::RatesTotal(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return 0; } //--- Return the number of bars in the timeseries of the received object return obj.RatesTotal(); } //+------------------------------------------------------------------+ //| Return the identifier of the specified indicator | //+------------------------------------------------------------------+ int CMSTFIndicators::ID(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return WRONG_VALUE; } //--- Return the identifier of the received object return obj.ID(); } //+------------------------------------------------------------------+ //| Return a description of the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::Description(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return the indicator description Otherwise error text return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Return the title of the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::Title(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return the indicator title Otherwise error text return(obj!=NULL ? obj.Title() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Return the category of the specified indicator | //+------------------------------------------------------------------+ ENUM_IND_CATEGORY CMSTFIndicators::Category(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return IND_CATEGORY_NONE; } //--- Return the category of the received object return obj.Category(); } //+------------------------------------------------------------------+ //| Return the number of parameters of the specified indicator | //+------------------------------------------------------------------+ uint CMSTFIndicators::ParamsTotal(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return 0; } //--- Return the number of parameters of the received object return obj.ParamsTotal(); } //+------------------------------------------------------------------+ //| Return the structure of parameters by index from the array | //| for the specified indicator | //+------------------------------------------------------------------+ MqlParam CMSTFIndicators::GetMqlParam(const int ind_handle,const int index) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); MqlParam null; ::ZeroMemory(null); return null; } //--- Return the structure of parameters of the received object by index from the array of parameters return obj.GetMqlParam(index); } //+------------------------------------------------------------------+ //| Return a timeframe description for the specified indicator | //+------------------------------------------------------------------+ string CMSTFIndicators::TimeframeDescription(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); //--- If the object is received, return a description of the indicator timeframe Otherwise error text return(obj!=NULL ? obj.Description() : ::StringFormat("%s: Failed to get indicator object",__FUNCTION__)); } //+------------------------------------------------------------------+ //| Return the amount of calculated data of the specified indicator | //+------------------------------------------------------------------+ int CMSTFIndicators::Calculated(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return WRONG_VALUE; } //--- Return the amount of calculated data of the received object return obj.Calculated(); } //+------------------------------------------------------------------+ //| Return the type of the specified indicator | //+------------------------------------------------------------------+ ENUM_INDICATOR CMSTFIndicators::Type(const int ind_handle) const { //--- Get a pointer to the indicator object using the handle passed to the method CIndMSTF *obj=this.GetIndicatorObj(ind_handle,__FUNCTION__); if(obj==NULL) { ::PrintFormat("%s: Failed to get indicator object",__FUNCTION__); return (ENUM_INDICATOR)WRONG_VALUE; } //--- Return the indicator type of the received object return (ENUM_INDICATOR)obj.Type(); }
Die Logik aller Methoden wird in der Methodenliste kommentiert. Zunächst erhalten wir einen Zeiger auf den gewünschten Indikator in der Liste und setzen oder geben dessen Eigenschaft zurück oder führen eine Berechnung durch und geben deren Ergebnis zurück. Alle aufgerufenen Methoden wurden oben bei der Diskussion der Basisklasse des Multi-Symbol- und Multi-Perioden-Indikators besprochen.
Die Implementierung von Methoden zur Erstellung neuer Indikatorobjekte:
//+------------------------------------------------------------------+ //| Add the specified indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewIndicator(CIndMSTF *ind_obj,const string source) { //--- Set the sorted list flag to the collection list this.m_list.Sort(); //--- Search the list for an index matching the indicator object passed to the method int index=this.m_list.Search(ind_obj); //--- If such an indicator with the same parameters is already in the list, if(index>WRONG_VALUE) { //--- report this to journal and delete the new indicator object ::PrintFormat("%s: The %s indicator with such parameters %s is already in the collection",source,ind_obj.Name(),ind_obj.Parameters()); delete ind_obj; //--- Get a pointer to an already existing indicator object in the list and return its handle ind_obj=this.m_list.At(index); return(ind_obj!=NULL ? ind_obj.Handle() : INVALID_HANDLE); } //--- If such an indicator is not in the list, but it could not be placed in the list if(!this.m_list.Add(ind_obj)) { //--- report the error in the journal, delete the indicator object and return INVALID_HANDLE ::PrintFormat("%s: Error. Failed to add %s indicator to collection",source,ind_obj.Name()); delete ind_obj; return INVALID_HANDLE; } //--- If indicator is placed in list, but creating a calculation part for it failed, return INVALID_HANDLE //--- (if there is an error creating a calculation part, the indicator object is deleted in the CreateIndicator method) if(!this.CreateIndicator(ind_obj)) return INVALID_HANDLE; //--- Successful - inform about addition of a new indicator to collection and return its handle ::PrintFormat("%s: %s indicator (handle %ld) added to the collection",source,ind_obj.Title(),ind_obj.Handle()); return ind_obj.Handle(); } //+------------------------------------------------------------------+ //| Add the Accelerator Oscillator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewAC(const string symbol,const ENUM_TIMEFRAMES timeframe) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndAC *ind_obj=new CIndAC(symbol,timeframe); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create AC indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Accumulation/Distribution indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewAD(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndAD *ind_obj=new CIndAD(symbol,timeframe,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create A/D indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+--------------------------------------------------------------------+ //| Add Average Directional Movement Index to the collection | //+--------------------------------------------------------------------+ int CMSTFIndicators::AddNewADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndADX *ind_obj=new CIndADX(symbol,timeframe,adx_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create ADX indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add to the collection the indicator | //| Average Directional Movement Index by Welles Wilder | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewADXWilder(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period=14) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndADXW *ind_obj=new CIndADXW(symbol,timeframe,adx_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create ADX Wilder indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Alligator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewAlligator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndAlligator *ind_obj=new CIndAlligator(symbol,timeframe,jaw_period,jaw_shift,teeth_period,teeth_shift,lips_period,lips_shift,ma_method,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Alligator indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Adaptive Moving Average indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ama_period=9, const int fast_ma_period=2, const int slow_ma_period=30, const int ama_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndAMA *ind_obj=new CIndAMA(symbol,timeframe,ama_period,fast_ma_period,slow_ma_period,ama_shift,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create AMA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Awesome Oscillator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewAO(const string symbol,const ENUM_TIMEFRAMES timeframe) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndAO *ind_obj=new CIndAO(symbol,timeframe); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create AO indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Average True Range indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewATR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndATR *ind_obj=new CIndATR(symbol,timeframe,ma_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create ATR indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Bears Power indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewBearsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndBears *ind_obj=new CIndBears(symbol,timeframe,ma_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Bears indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Bulls Power indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewBullsPower(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=13) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndBulls *ind_obj=new CIndBulls(symbol,timeframe,ma_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Bulls indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Bollinger Bands® indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewBands(const string symbol,const ENUM_TIMEFRAMES timeframe, const int bands_period=20, const int bands_shift=0, const double deviation=2.0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndBands *ind_obj=new CIndBands(symbol,timeframe,bands_period,bands_shift,deviation,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Bands indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Commodity Channel Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewCCI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_TYPICAL) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndCCI *ind_obj=new CIndCCI(symbol,timeframe,ma_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create CCI indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Chaikin Oscillator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewChaikin(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ma_period=3, const int slow_ma_period=10, const ENUM_MA_METHOD ma_method=MODE_EMA, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndCHO *ind_obj=new CIndCHO(symbol,timeframe,fast_ma_period,slow_ma_period,ma_method,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Chaikin indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+-------------------------------------------------------------------+ //| Add Double Exponential Moving Average to the collection | //+-------------------------------------------------------------------+ int CMSTFIndicators::AddNewDEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndDEMA *ind_obj=new CIndDEMA(symbol,timeframe,ma_period,ma_shift,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create DEMA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the DeMarker indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewDeMarker(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=14) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndDeM *ind_obj=new CIndDeM(symbol,timeframe,ma_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create DeMarker indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Envelopes indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewEnvelopes(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE, const double deviation=0.1) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndEnvelopes *ind_obj=new CIndEnvelopes(symbol,timeframe,ma_method,ma_shift,ma_method,applied_price,deviation); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Envelopes indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Force Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewForce(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=13, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndForce *ind_obj=new CIndForce(symbol,timeframe,ma_period,ma_method,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Force indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Fractals indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewFractals(const string symbol,const ENUM_TIMEFRAMES timeframe) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndFractals *ind_obj=new CIndFractals(symbol,timeframe); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Fractals indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Fractal Adaptive Moving Average indicator to collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewFrAMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndFrAMA *ind_obj=new CIndFrAMA(symbol,timeframe,ma_period,ma_shift,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create FrAMA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Gator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewGator(const string symbol,const ENUM_TIMEFRAMES timeframe, const int jaw_period=13, const int jaw_shift=8, const int teeth_period=8, const int teeth_shift=5, const int lips_period=5, const int lips_shift=3, const ENUM_MA_METHOD ma_method=MODE_SMMA, const ENUM_APPLIED_PRICE applied_price=PRICE_MEDIAN) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndGator *ind_obj=new CIndGator(symbol,timeframe,jaw_period,jaw_shift,teeth_period,teeth_shift,lips_period,lips_shift,ma_method,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Gator indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Ichimoku Kinko Hyo indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewIchimoku(const string symbol,const ENUM_TIMEFRAMES timeframe, const int tenkan_sen=9, const int kijun_sen=26, const int senkou_span_b=52) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndIchimoku *ind_obj=new CIndIchimoku(symbol,timeframe,tenkan_sen,kijun_sen,senkou_span_b); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Ichimoku indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Market Facilitation Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewBWMFI(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndBWMFI *ind_obj=new CIndBWMFI(symbol,timeframe,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create BW MFI indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Momentum indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewMomentum(const string symbol,const ENUM_TIMEFRAMES timeframe, const int mom_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndMomentum *ind_obj=new CIndMomentum(symbol,timeframe,mom_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Momentum indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Money Flow Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewMFI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndMFI *ind_obj=new CIndMFI(symbol,timeframe,ma_period,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create MFI indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Moving Average indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=10, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndMA *ind_obj=new CIndMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create MA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Moving Average of Oscillator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewOsMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period=12, const int slow_ema_period=26, const int signal_period=9, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndOsMA *ind_obj=new CIndOsMA(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create OsMA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add to the collection the indicator | //| Moving Averages Convergence/Divergence | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewMACD(const string symbol,const ENUM_TIMEFRAMES timeframe, const int fast_ema_period=12, const int slow_ema_period=26, const int signal_period=9, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndMACD *ind_obj=new CIndMACD(symbol,timeframe,fast_ema_period,slow_ema_period,signal_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create MACD indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the On Balance Volume indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewOBV(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndOBV *ind_obj=new CIndOBV(symbol,timeframe,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create OBV indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+-------------------------------------------------------------------+ //| Add Parabolic Stop and Reverse system to the collection | //+-------------------------------------------------------------------+ int CMSTFIndicators::AddNewSAR(const string symbol,const ENUM_TIMEFRAMES timeframe, const double step=0.02, const double maximum=0.2) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndSAR *ind_obj=new CIndSAR(symbol,timeframe,step,maximum); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create SAR indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Relative Strength Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewRSI(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndRSI *ind_obj=new CIndRSI(symbol,timeframe,ma_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create RSI indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Relative Vigor Index indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewRVI(const string symbol,const ENUM_TIMEFRAMES timeframe,const int ma_period=10) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndRVI *ind_obj=new CIndRVI(symbol,timeframe,ma_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create RVI indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Standard Deviation indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewStdDev(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=20, const int ma_shift=0, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndStdDev *ind_obj=new CIndStdDev(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create StdDev indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Stochastic Oscillator indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewStochastic(const string symbol,const ENUM_TIMEFRAMES timeframe, const int Kperiod=5, const int Dperiod=3, const int slowing=3, const ENUM_MA_METHOD ma_method=MODE_SMA, const ENUM_STO_PRICE price_field=STO_LOWHIGH) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndStoch *ind_obj=new CIndStoch(symbol,timeframe,Kperiod,Dperiod,slowing,ma_method,price_field); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Stochastic indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+-------------------------------------------------------------------+ //| Add Triple Exponential Moving Average indicator to the collection | //+-------------------------------------------------------------------+ int CMSTFIndicators::AddNewTEMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndTEMA *ind_obj=new CIndTEMA(symbol,timeframe,ma_period,ma_shift,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create TEMA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add to the collection the indicator | //| Triple Exponential Moving Averages Oscillator | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewTriX(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period=14, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndTriX *ind_obj=new CIndTriX(symbol,timeframe,ma_period,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create TriX indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add Larry Williams' Percent Range to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewWPR(const string symbol,const ENUM_TIMEFRAMES timeframe,const int calc_period=14) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndWPR *ind_obj=new CIndWPR(symbol,timeframe,calc_period); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create WPR indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add Variable Index Dynamic Average to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewVIDyA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int cmo_period=9, const int ema_period=12, const int ma_shift=0, const ENUM_APPLIED_PRICE applied_price=PRICE_CLOSE) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndVIDyA *ind_obj=new CIndVIDyA(symbol,timeframe,cmo_period,ema_period,ma_shift,applied_price); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create VIDyA indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add the Volumes indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewVolumes(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_APPLIED_VOLUME applied_volume=VOLUME_TICK) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndVolumes *ind_obj=new CIndVolumes(symbol,timeframe,applied_volume); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create Volumes indicator object",__FUNCTION__); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); } //+------------------------------------------------------------------+ //| Add a custom indicator to the collection | //+------------------------------------------------------------------+ int CMSTFIndicators::AddNewCustom(const string symbol,const ENUM_TIMEFRAMES timeframe,const string path,const string name,const uint buffers,const MqlParam ¶m[]) { //--- Create a new indicator object. If there is an error, add a journal message and return INVALID_HANDLE CIndCustom *ind_obj=new CIndCustom(symbol,timeframe,path,name,buffers,param); if(ind_obj==NULL) { ::PrintFormat("%s: Error. Failed to create %s custom indicator object",__FUNCTION__,name); return INVALID_HANDLE; } //--- Return the result of adding the created indicator object to the collection list return this.AddNewIndicator(ind_obj,__FUNCTION__); }
Alle Codes für die Klasse der Indikatorkollektion sind fertig. Jede Methode ist mit Kommentaren versehen, die ihre Logik beschreiben, sodass sie leicht zu verstehen sind.
Tests
Um die Klassen der Multi-Indikatoren und ihre Kollektion zu testen, erstellen wir einen einfachen Indikator. Der Indikator zeigt eine Auswahl aus mehreren gleitenden Durchschnitten an. Der Zweck dieser Tests ist es, die Funktionsweise von Klassen mit Indikatoren zu überprüfen, die eine Linie von einem Puffer im Hauptdiagrammfenster zeichnen. Zu diesem Zweck werden am besten gleitende Durchschnitte aus dem Satz der Trendindikatoren im Client-Terminal verwendet. In den nächsten Artikeln werden wir Vorlagen für die schnelle Erstellung und die Arbeit mit beliebigen Indikatoren aus dem Standardset des Terminals und die Arbeit mit nutzerdefinierten Indikatoren erstellen. Nach und nach werden wir die heute erstellten Klassen vollständig fertigstellen, damit sie mit allen Arten von Indikatoren korrekt funktionieren.
Der Testindikator zeichnet die Linien der erstellten Multi-Indikatoren auf dem Chart. Die entsprechenden Daten werden auf dem Dashboard angezeigt. Die Erstellung dieses Dashboards wurde bereits im ersten Artikel dieser Reihe beschrieben.
Wir werden zwei identische Indikatoren innerhalb eines Indikators erstellen. Eine davon wird anhand der aktuellen Chartdaten berechnet, die zweite anhand der Daten des in den Einstellungen ausgewählten Symbols/Periode. Auf diese Weise sehen Sie immer die Linie des gleitenden Durchschnitts im aktuellen Chart und die Linie desselben gleitenden Durchschnitts, die jedoch mit Daten einer anderen Chartperiode erstellt wurde. Es ist möglich, ein Symbol aus einem anderen Chart auszuwählen, aber dann stimmen die Linien nicht mit dem Preisniveau überein.
Erstellen wir eine neue Indikatordatei mit dem Namen TestMSTFMovingAverages.mq5:
Wählen Sie die Ereignisbehandlungen OnTimer und OnChartEvent aus:
Wählen Sie zwei Puffer aus, die als Linien im Hauptfenster des Charts gezeichnet werden sollen:
Sie können beliebige Puffernamen verwenden, da sie im Code umbenannt werden. Klicken Sie auf „Fertigstellen“ und Sie erhalten eine Indikatorvorlage:
//+------------------------------------------------------------------+ //| TestMSTFMovingAverages.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 //--- plot MA1 #property indicator_label1 "MA1" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot MA2 #property indicator_label2 "MA2" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- indicator buffers double MA1Buffer[]; double MA2Buffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,MA1Buffer,INDICATOR_DATA); SetIndexBuffer(1,MA2Buffer,INDICATOR_DATA); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- //--- return value of prev_calculated for the next call return(rates_total); } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- }
Fügen Sie im globalen Bereich eine Enumeration hinzu, um den Typ des gleitenden Durchschnitts auszuwählen, binden Sie die Klassendatei der Multi-Symbol-Multi-Perioden-Indikatoren und die Dashboard-Klassendatei ein und deklarieren Sie Eingabevariablen.
Benennen Sie die Puffer um, um eine aussagekräftigere Darstellung zu erhalten, und deklarieren Sie die globale Indikatorvariablen:
//+------------------------------------------------------------------+ //| TestMSTFMovingAverages.mq5 | //| Copyright 2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #property indicator_chart_window #property indicator_buffers 2 #property indicator_plots 2 //--- enums enum ENUM_USED_MA { USED_MA_AMA = IND_AMA, // Adaptive Moving Average USED_MA_DEMA = IND_DEMA, // Double Exponential Moving Average USED_MA_FRAMA = IND_FRAMA, // Fractal Adaptive Moving Average USED_MA_MA = IND_MA, // Moving Average USED_MA_TEMA = IND_TEMA, // Triple Exponential Moving Average USED_MA_VIDYA = IND_VIDYA, // Variable Index Dynamic Average }; //--- plot MA1 #property indicator_label1 "MA1" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plot MA2 #property indicator_label2 "MA2" #property indicator_type2 DRAW_LINE #property indicator_color2 clrRed #property indicator_style2 STYLE_SOLID #property indicator_width2 1 //--- includes #include <IndMSTF\IndMSTF.mqh> #include <Dashboard\Dashboard.mqh> //--- input parameters input ENUM_USED_MA InpIndicator = USED_MA_MA; /* Used MA */ // Type of moving average to use input string InpSymbol = NULL; /* Symbol */ // Moving average symbol input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT; /* Timeframe */ // Moving average timeframe input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE; /* Applied Price */ // Price used for calculations input ENUM_MA_METHOD InpMethod = MODE_SMA; /* MA Method */ // Moving Average calculation method input int InpShift = 0; /* MA Shift */ // Moving average shift input bool InpAsSeries = true; /* As Series flag */ // Timeseries flag of indicator buffer arrays //--- indicator buffers double BufferMA1[]; double BufferMA2[]; //--- global variables int handle_ma1; int handle_ma2; CMSTFIndicators indicators; // An instance of the indicator collection object //--- variables for the panel CDashboard *panel=NULL; // Pointer to the panel object int mouse_bar_index; // Index of the bar the data is taken from
Innerhalb von OnInit() geben Sie die Erstellung eines Timers ein, weisen Zeichnungspuffer zu, erstellen Handles für die ausgewählten Indikatoren und erstellen ein Dashboard:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Set a timer with an interval of 1 second EventSetTimer(1); //--- Assign the BufferMA1 and BufferMA2 arrays to the plot buffers 0 and 1, respectively SetIndexBuffer(0,BufferMA1,INDICATOR_DATA); SetIndexBuffer(1,BufferMA2,INDICATOR_DATA); //--- sets indicator shift //PlotIndexSetInteger(0,PLOT_SHIFT,InpShift); // analog in line 116 //PlotIndexSetInteger(1,PLOT_SHIFT,InpShift); // analog in line 117 //--- Set the timeseries flags for the indicator buffer arrays (for testing, to see that there is no difference) ArraySetAsSeries(BufferMA1,InpAsSeries); ArraySetAsSeries(BufferMA2,InpAsSeries); //--- For different indicators, the dashboard width will be individual (due to the number of parameters in the description) int width=0; //--- According on the indicator selected in the settings, create two indicators of the same type //--- The first one is calculated on the current chart symbol/period, the second - on those specified in the settings switch(InpIndicator) { case USED_MA_AMA : handle_ma1=indicators.AddNewAMA(NULL,PERIOD_CURRENT,9,2,30,InpShift); handle_ma2=indicators.AddNewAMA(InpSymbol,InpTimeframe,9,2,30,InpShift); width=269; break; case USED_MA_DEMA : handle_ma1=indicators.AddNewDEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice); handle_ma2=indicators.AddNewDEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice); width=255; break; case USED_MA_FRAMA : handle_ma1=indicators.AddNewFrAMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice); handle_ma2=indicators.AddNewFrAMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice); width=259; break; case USED_MA_TEMA : handle_ma1=indicators.AddNewTEMA(NULL,PERIOD_CURRENT,14,InpShift,InpPrice); handle_ma2=indicators.AddNewTEMA(InpSymbol,InpTimeframe,14,InpShift,InpPrice); width=253; break; case USED_MA_VIDYA : handle_ma1=indicators.AddNewVIDyA(NULL,PERIOD_CURRENT,9,12,InpShift,InpPrice); handle_ma2=indicators.AddNewVIDyA(InpSymbol,InpTimeframe,9,12,InpShift,InpPrice); width=267; break; default: handle_ma1=indicators.AddNewMA(NULL,PERIOD_CURRENT,10,InpShift,InpMethod,InpPrice); handle_ma2=indicators.AddNewMA(InpSymbol,InpTimeframe,10,InpShift,InpMethod,InpPrice); width=231; break; } //--- If failed to create indicator handles, return initialization error if(handle_ma1==INVALID_HANDLE || handle_ma2==INVALID_HANDLE) return INIT_FAILED; //--- Set descriptions for indicator lines from buffer descriptions of calculation part of created indicators indicators.SetPlotLabelFromBuffer(0,handle_ma1,0); indicators.SetPlotLabelFromBuffer(1,handle_ma2,0); //--- Set "empty" values for calculation part buffers of the created indicators indicators.SetBufferInitValue(handle_ma1,0,EMPTY_VALUE); indicators.SetBufferInitValue(handle_ma2,0,EMPTY_VALUE); //--- Set shifts for indicator lines indicators.SetPlotShift(0,InpShift); indicators.SetPlotShift(1,InpShift); //--- Dashboard //--- Create the panel panel=new CDashboard(1,20,20,width,264); if(panel==NULL) { Print("Error. Failed to create panel object"); return INIT_FAILED; } //--- Set font parameters panel.SetFontParams("Calibri",9); //--- Display the panel with the "Symbol, Timeframe description" header text panel.View(Symbol()+", "+StringSubstr(EnumToString(Period()),7)); //--- Create a table with ID 0 to display bar data in it panel.CreateNewTable(0); //--- Draw a table with ID 0 on the dashboard background panel.DrawGrid(0,2,20,6,2,18,width/2-2); //--- Create a table with ID 1 to display the data of indicator 1 panel.CreateNewTable(1); //--- Get the Y2 table coordinate with ID 0 and //--- set the Y1 coordinate for the table with ID 1 int y1=panel.TableY2(0)+22; //--- Draw a table with ID 1 on the dashboard background panel.DrawGrid(1,2,y1,2,2,18,width/2-2); //--- Create a table with ID 2 to display the data of indicator 2 panel.CreateNewTable(2); //--- Get the Y2 coordinate of the table with ID 1 and //--- set the Y1 coordinate for the table with ID 2 int y2=panel.TableY2(1)+3; //--- Draw a table with ID 2 on the background of the dashboard panel.DrawGrid(2,2,y2,3,2,18,width/2-2); //--- Initialize the variable with the index of the mouse cursor bar mouse_bar_index=0; //--- Display the data of the current bar on the dashboard DrawData(mouse_bar_index,TimeCurrent()); //--- Successful initialization return(INIT_SUCCEEDED); }
Fügen Sie OnDeinit() hinzu:
//+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Delete the timer EventKillTimer(); //--- If the panel object exists, delete it if(panel!=NULL) delete panel; //--- Delete all comments Comment(""); }
Rufen Sie im OnCalculate() die Berechnung aller Multi-Symbol-Multi-Perioden-Indikatoren auf. Wenn die Berechnung fehlschlägt, wird der Handler beendet und Null zurückgegeben, um die Indikatoren beim nächsten Tick neu zu berechnen.
Nach erfolgreicher Berechnung sind die Daten in den Array-Puffern der Indikatorobjekte bereits vorhanden und können im Dashboard angezeigt werden. Nachdem Sie die Daten auf dem Dashboard angezeigt haben, geben Sie die Daten aus den berechneten Puffern in die Zeichnungspuffer des Indikators aus:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- Number of bars for calculation int limit=rates_total-prev_calculated; //--- If limit > 1, then this is the first calculation or change in the history if(limit>1) { //--- specify all the available history for calculation limit=rates_total-1; /* // If the indicator has any buffers that display other calculations (not multi-indicators), // initialize them here with the "empty" value set for these buffers */ } //--- Calculate all created multi-symbol multi-period indicators if(!indicators.Calculate()) return 0; //--- Display the bar data under cursor (or current bar if cursor is outside the chart) on the dashboard DrawData(mouse_bar_index,time[mouse_bar_index]); //--- From buffers of calculated indicators, output data to indicator buffers if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma1,0,limit,BufferMA1)) return 0; if(!indicators.DataToBuffer(NULL,PERIOD_CURRENT,handle_ma2,0,limit,BufferMA2)) return 0; //--- return value of prev_calculated for the next call return(rates_total); }
Rufen Sie im Timer des Indikators den Timer des Kollektionsobjekts des Indikators auf:
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- Call the indicator collection timer indicators.OnTimer(); }
Rufen Sie in OnChartEvent() die Ereignisbehandlung des Dashboard-Objekts auf und verarbeiten Sie die Bewegungen des Cursors, um den Balken zu bestimmen, an dem er sich befindet:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- Handling the panel //--- Call the panel event handler panel.OnChartEvent(id,lparam,dparam,sparam); //--- If the cursor moves or a click is made on the chart if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK) { //--- Declare the variables to record time and price coordinates in them datetime time=0; double price=0; int wnd=0; //--- If the cursor coordinates are converted to date and time if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price)) { //--- write the bar index where the cursor is located to a global variable mouse_bar_index=iBarShift(Symbol(),PERIOD_CURRENT,time); //--- Display the bar data under the cursor on the panel DrawData(mouse_bar_index,time); } } //--- If we received a custom event, display the appropriate message in the journal if(id>CHARTEVENT_CUSTOM) { //--- Here we can implement handling a click on the close button on the panel PrintFormat("%s: Event id=%ld, object id (lparam): %lu, event message (sparam): %s",__FUNCTION__,id,lparam,sparam); } }
Funktion zur Anzeige von Multi-Indikator-Daten auf dem Dashboard:
//+------------------------------------------------------------------+ //| Display data from the specified timeseries index to dashboard | //+------------------------------------------------------------------+ void DrawData(const int index,const datetime time) { //--- Declare the variables to receive data in them MqlRates rates[1]; //--- Exit if unable to get the bar data by the specified index if(CopyRates(Symbol(),PERIOD_CURRENT,index,1,rates)!=1) return; //--- Set font parameters for bar and indicator data headers int size=0; uint flags=0; uint angle=0; string name=panel.FontParams(size,flags,angle); panel.SetFontParams(name,9,FW_BOLD); panel.DrawText("Bar data ["+(string)index+"]",3,panel.TableY1(0)-16,clrMaroon,panel.Width()-6); panel.DrawText("Indicators data ["+(string)index+"]",3,panel.TableY1(1)-16,clrGreen,panel.Width()-6); //--- Set font parameters for bar and indicator data panel.SetFontParams(name,9); //--- Display the data of the specified bar in table 0 on the panel panel.DrawText("Date", panel.CellX(0,0,0)+2, panel.CellY(0,0,0)+2); panel.DrawText(TimeToString( rates[0].time,TIME_DATE), panel.CellX(0,0,1)+2, panel.CellY(0,0,1)+2,clrNONE,90); panel.DrawText("Time", panel.CellX(0,1,0)+2, panel.CellY(0,1,0)+2); panel.DrawText(TimeToString( rates[0].time,TIME_MINUTES), panel.CellX(0,1,1)+2, panel.CellY(0,1,1)+2,clrNONE,90); panel.DrawText("Open", panel.CellX(0,2,0)+2, panel.CellY(0,2,0)+2); panel.DrawText(DoubleToString(rates[0].open,Digits()), panel.CellX(0,2,1)+2, panel.CellY(0,2,1)+2,clrNONE,90); panel.DrawText("High", panel.CellX(0,3,0)+2, panel.CellY(0,3,0)+2); panel.DrawText(DoubleToString(rates[0].high,Digits()), panel.CellX(0,3,1)+2, panel.CellY(0,3,1)+2,clrNONE,90); panel.DrawText("Low", panel.CellX(0,4,0)+2, panel.CellY(0,4,0)+2); panel.DrawText(DoubleToString(rates[0].low,Digits()), panel.CellX(0,4,1)+2, panel.CellY(0,4,1)+2,clrNONE,90); panel.DrawText("Close", panel.CellX(0,5,0)+2, panel.CellY(0,5,0)+2); panel.DrawText(DoubleToString(rates[0].close,Digits()), panel.CellX(0,5,1)+2, panel.CellY(0,5,1)+2,clrNONE,90); //--- Output the data of indicator 1 from the specified bar into table 1 panel.DrawText(indicators.Title(handle_ma1), panel.CellX(1,0,0)+2, panel.CellY(1,0,0)+2); double value1=indicators.GetData(handle_ma1,0,index); string value_str1=(value1!=EMPTY_VALUE ? DoubleToString(value1,indicators.Digits(handle_ma1)) : " "); panel.DrawText(value_str1,panel.CellX(1,0,1)+2,panel.CellY(1,0,1)+2,clrNONE,110); //--- Display a description of the indicator 1 line state panel.DrawText("Line state", panel.CellX(1,1,0)+2, panel.CellY(1,1,0)+2); ENUM_LINE_STATE state1=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma1,0,index); panel.DrawText(BufferLineStateDescription(state1),panel.CellX(1,1,1)+2,panel.CellY(1,1,1)+2,clrNONE,110); //--- Output the data of indicator 2 from the specified bar into table 2 panel.DrawText(indicators.Title(handle_ma2), panel.CellX(2,0,0)+2, panel.CellY(2,0,0)+2); double value2=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,index); string value_str2=(value2!=EMPTY_VALUE ? DoubleToString(value2,indicators.Digits(handle_ma2)) : " "); panel.DrawText(value_str2,panel.CellX(2,0,1)+2,panel.CellY(2,0,1)+2,clrNONE,110); //--- Display a description of the indicator 2 line state panel.DrawText("Line state", panel.CellX(2,1,0)+2, panel.CellY(2,1,0)+2); ENUM_LINE_STATE state2=indicators.BufferLineState(Symbol(),PERIOD_CURRENT,handle_ma2,0,index); panel.DrawText(BufferLineStateDescription(state2),panel.CellX(2,1,1)+2,panel.CellY(2,1,1)+2,clrNONE,110); //--- Display description of relationship between indicator 1 line relative to indicator 2 line double value21=indicators.GetDataTo(Symbol(),PERIOD_CURRENT,handle_ma2,0,index+1); ENUM_LINE_STATE stateR=indicators.BufferLineStateRelative(Symbol(),PERIOD_CURRENT,handle_ma1,0,index,value2,value21); string ma1=indicators.Name(handle_ma1); string ma2=indicators.Name(handle_ma2); string state_relative= ( stateR==LINE_STATE_ABOVE ? StringFormat("%s1 > %s2",ma1,ma2) : stateR==LINE_STATE_BELOW ? StringFormat("%s1 < %s2",ma1,ma2) : stateR==LINE_STATE_CROSS_DOWN ? "Top-down crossing" : stateR==LINE_STATE_CROSS_UP ? "Bottom-up crossing" : BufferLineStateDescription(stateR) ); panel.DrawText(StringFormat("%s1 vs %s2",ma1,ma2), panel.CellX(2,2,0)+2, panel.CellY(2,2,0)+2); panel.DrawText(state_relative, panel.CellX(2,2,1)+2, panel.CellY(2,2,1)+2,clrNONE,110); //--- Redraw the chart to immediately display all changes on the panel ChartRedraw(ChartID()); }
Wie Sie sehen, können wir Indikatoren mehrerer Symbole und mehrerer Zeitrahmen berechnen, indem wir einfach die Methode Calculate() der Klasse der Indikatorkollektion aufrufen. Nach erfolgreicher Berechnung sind alle Daten bereits verfügbar. Diese Daten können von Expert Advisors abgerufen und verarbeitet werden. In Indikatoren können die Daten nach erfolgreicher Berechnung mit der Methode DataToBuffer() der Indikatorenkollektionsklasse in einem Chart in Form von Linien dargestellt werden. Das ist alles, was Sie brauchen, um Multi-Indikatoren zu berechnen und im Chart anzuzeigen.
Nachdem wir den Testindikator kompiliert haben, starten wir ihn auf einem Chart mit der Periode M1. In den Einstellungen wählen wir das aktuelle Symbol und die Berechnungsperiode des Indikators M5. In diesem Fall werden zwei gleitende Durchschnitte, die in den Indikatoreinstellungen ausgewählt wurden, erstellt. Einer davon wird anhand der Daten des aktuellen Charts berechnet, der zweite basiert auf den Daten des 5-Minuten Zeitrahmen. Wenn Sie den Zeitrahmen des Charts wechseln, können Sie sehen, wie zwei Linien auf M1 gezeichnet werden: eine entspricht dem auf M1 berechneten gleitenden Durchschnitt und die zweite Linie entspricht dem auf M5 berechneten gleitenden Durchschnitt. Wenn Sie das Chart auf M5 umstellen, wird nur ein Indikator erstellt, da der zweite Indikator mit dem ersten identisch ist und nicht erstellt wird. Wenn Sie das Chart auf M15 umstellen, wird ein Indikator für M15 und der zweite für M5 berechnet und auch im Chart angezeigt.
Wie Sie sehen können, funktioniert die angegebene Funktionalität und wir können die Indikatoren im Hauptfenster des Charts sehen. Mehrere Indikatoren werden unter Verwendung eines Puffers erstellt.
Die Dateien aller Klassen und der Testindikator sind im Anhang verfügbar.
Schlussfolgerung
Heute haben wir eine Funktion geschaffen, mit der Sie schnell Multi-Symbol-Multi-Perioden-Indikatoren erstellen und ihre Daten in Indikatoren empfangen sowie die berechneten Indikatoren im Hauptdiagrammfenster darstellen können. In den folgenden Artikeln werden wir Vorlagen für die Erstellung anderer Multi-Symbol- und Multi-Perioden-Standardindikatoren erstellen, die ihre Daten im Hauptdiagrammfenster und nicht nur mit einem Puffer darstellen. Als Ergebnis erhalten wir ein praktisches Werkzeug, um beliebige Indikatoren schnell in Multi-Symbol- und Multi-Perioden-Versionen zu verwandeln. Die Multi-Indikator-Klassen werden wahrscheinlich weiterentwickelt werden, da wir weitere Standard- und nutzerdefinierte Indikatoren erstellen werden.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/13578





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