MQL4 könyv    Egyszerű MQL4 programok   Az egyéni indikátorok létrehozása

Az egyéni indikátorok létrehozása

Egy kereskedési stratégia megalkotása során a fejlesztő gyakran találkozik azzal a szükséglettel, hogy a felhasználó (programozó) által meghatározott függőséget grafikusan megjelenítse a szimbólum ablakban. Ezért az MQL4-ben is lehetőség van egyéni indikátorok létrehozására.

Custom Indicator Egyéni Indikátor egy MQL4 kódú alkalmazási program, aminek alapvető célja az előzetesen meghatározott függőség grafikus ábrázolása.

Az egyéni indikátor szerkezete

A bufferek szerepe

A egyéni indikátorok fő elve átadni az indikátorvonalak értékeit az ügyfélterminálnak (az indikátorvonalak megrajzolása céljából) a buffereken keresztül.

Buffer egy memória terület, ami tartalmazza egy indikátorvonal számszerű értékeit.

Az MQL4 lehetőséget biztosít arra, hogy egy egyéni indikátor akár nyolc indikátorvonalat rajzoljon. Mindegyik indikátor tömb egy bufferen keresztül kapcsolódik  egy indikátorvonalhoz. Mindegyik buffernek van saját indexe. Az első buffer indexe 0, a másodiké - 1 és így tovább az utolsó indexe 7. 115. ábrán látható, hogy az egyéni indikátorból származó információt hogyan adjuk át a buffereken keresztül az ügyfélterminálnak indikátor vonalak rajzolása céljából.


115. ábra. Az indikátor tömbök értékeinek átadása egy bufferen keresztül az ügyfélterminálnak.

Az indikátor vonalak létrehozásának általános rendje a következő:

1. A számításokat elvégezzük az egyéni indikátorban; a kapott számszerű értékeket adjuk az indikátor tömb elemeknek.

2. Az indikátor tömb elemek értékeit elküldjük a buffereken keresztül az ügyfélterminálnak.

3. A bufferektől kapott értékek alapján az ügyfélterminál megrajzolja az indikátorvonalakat.

Az egyéni indikátorok összetevői

Elemezzünk egy olyan egyszerű egyéni indikátort, amely két indikátorvonalat használ, - az első vonal a bárok legnagyobb, a második a bárok legkisebb értékét mutatja.

Példa

Példa egy egyszerű egyéni indikátorra:  userindicator.mq4


//--------------------------------------------------------------------
// userindicator.mq4 
// The code should be used for educational purpose only.
//--------------------------------------------------------------------
#property indicator_chart_window // Indicator is drawn in the main window
#property indicator_buffers 2 // Number of buffers
#property indicator_color1 Blue // Color of the 1st line
#property indicator_color2 Red // Color of the 2nd line
 
double Buf_0[],Buf_1[]; // Declaring arrays (for indicator buffers)
//--------------------------------------------------------------------
int init() // Special function init()
 {
 SetIndexBuffer(0,Buf_0); // Assigning an array to a buffer
 SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style
 SetIndexBuffer(1,Buf_1); // Assigning an array to a buffer
 SetIndexStyle (1,DRAW_LINE,STYLE_DOT,1);// Line style
 return; // Exit the special funct. init()
 }
//--------------------------------------------------------------------
int start() // Special function start()
 {
 int i, // Bar index
 Counted_bars; // Number of counted bars
//--------------------------------------------------------------------
 Counted_bars=IndicatorCounted(); // Number of counted bars
 i=MathMin(Bars-Counted_bars-1,Bars-Aver_Bars-1); // Index of the first uncounted
 while(i>=0) // Loop for uncounted bars
 {
 Buf_0[i]=High[i]; // Value of 0 buffer on i bar
 Buf_1[i]=Low[i]; // Value of 1st buffer on i bar
 i--; // Calculating index of the next bar
 }
//--------------------------------------------------------------------
 return; // Exit the special funct. start()
 }
//--------------------------------------------------------------------

Elemezzük egyenként az indikátor részeit. Minden MQL4-ben írt alkalmazási program olyan beállítási paramétereket tartalmaz, amely paraméterek lehetővé teszik az ügyfélterminálnak a megfelelő programvégrehajtást. Ebben a példában a programfejrész (lásd:  Programszerkezet) több #property utasítást tartalmaz.

Az első utasítás azt jelzi, hogy az ügyfélterminálnak melyik ablakban kell megjelenítenie az indikátorvonalakat:

#property indicator_chart_window  // Indicator is drawn in the main window

Az MQL4-ben indikátorok megjelenítésére két lehetőség van: a fő szimbólum ablakban és egy különálló ablakban. Fő ablak az az ablak, ami a szimbólum árfolyamábrát tartalmazza. Ebben a példában a #property indicator_chart_window azt jelzi, hogy az indikátort az ügyfélterminálnak a fő ablakban kell rajzolnia.

A következő sor az indikátorban használ a bufferek számát mutatja:

#property indicator_buffers 2 // Number of buffers

A vizsgált példában két indikátor vonalat rajzolunk. Itt megadjuk a használt bufferek számát, ami jelenleg kettő.

A következő sorok leírják az indikátor vonalak színeit.

#property indicator_color1 Blue  // Color of the 1st line
#property indicator_color2 Red // Color of the 2nd line

Az indicator_color1 és indicator_color2 paraméterek azokat a színeket határozzák meg, amelyekkel a megfelelő bufferek tartalmát ábrázoljuk, - ebben az esetben 0 indexű buffer kék (Blue) és az 1-es piros (Red). Vegyük észre, hogy az indicator_color1 és indicator_color2 paraméterneveket használjuk és nem a buffer indexeket. Ezek olyan állandók nevei, amik a bufferekkel összhangban vannak elnevezve. Mindegyik szín állandót a felhasználó saját belátása szerint állíthat be.

A következő sorokban indikátor tömböket deklaráljuk:

double Buf_0[],Buf_1[];  // Declaring arrays (for indicator buffers)

Az indikátor feladata, hogy két indikátor vonalat rajzoljon, úgyhogy nekünk két darab egy dimenziós tömböt kell deklarálnunk, vonalanként egyet. Az indikátor vonalak neveit a felhasználó választja meg. Ebben az esetben a tömbök neve Buf_0[] és Buf_1[], másik esetben másik neveket használhatunk, például: Line_1[],Alfa[], Integral[] , stb. A tömböket globális szinten kell deklarálni, hogy a tömbelem  értékek megmaradjanak a start() különleges függvény hívásai között.

A leírt egyéni indikátor alapja két különleges függvény az init() és a start(). Az  init() függvény a kódnak azt a részét tartalmazza, amit a program csak egyszer használ, (lásd:  Különleges függvények).

Egy nagyon fontos dolog történik a következő sorban:

 SetIndexBuffer(0,Buf_0);  // Assigning an array to a buffer

A SetIndexBuffer() függvénnyel a megfelelő buffert (ebben az esetben a 0 indexűt) társítjuk egy tömbhöz (ebben az esetben Buf_0). Ennek következtében az ügyfélterminál az első indikátor vonal létrehozásához azokat az adatokat fogja használni, amiket a Buf_0 tömb tartalmaz.

Majd meghatározzuk a vonalstílust:

 SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style

A null buffer (0) értékeinek rajzolásához az ügyfélterminálnak a következő stílusokat kell használnia: egyszerű vonal (DRAW_LINE), folytonos vonal (STYLE_SOLID), vonalvastagság: 2.

A következő két sor a második vonal paramétereit tartalmazza:

 SetIndexBuffer(1,Buf_1);  // Assigning an array to a buffer
 SetIndexStyle (1,DRAW_LINE,STYLE_DOT,1); // Line style

A kód alapján az init() függvény mindkét indikátorvonalat a főablakban jeleníti meg. Az első 2-es szélességgel egy folytonos kék vonal lesz, a második egy pontozott piros vonal ( STYLE_DOT) normál szélességgel. Indikátor vonalakat más stílussal is rajzolhatunk (lásd: Az indikátorvonalak stílusai).

Az indikátor tömbelemek értékeinek kiszámítása (figyelj oda)

Az indikátor tömb elemek értéket a start() különleges függvényben számoljuk ki. A start() kódjának megértéséhez figyeljük meg a bárok indexelését. A Tömbök 

részben részletesen leírtuk az előre definiált tömbök indexelési módszerét. E szerint a módszer szerint az indexelést a nullától kezdjük. A null bár a jelenleg kialakulóban lévő bár. A legközelebbi bár indexe 1. A z előzőé 2 és így tovább.

Ahogy az új bárok megjelennek az ablakban, a már kialakult (történelmi) bárok indexei megváltoznak. Az új (aktuális, kialakuló, jobboldali) bár kapja a nulla indexet, a tőle balra lévő (a legutoljára kialakított) kapja az 1-es index értéket és minden történelmi bár indexe szintén növekszik eggyel.

Figyelem!

A bárok indexelésének ez az egyetlen módszere, ami lehetővé teszi a MetaTrader online kereskedő rendszer számára, hogy a technikai és egyéni indikátorokat egyaránt fel tudja rajzolni.

Azt már korábban elmondtuk, hogy az indikátor vonalakat azok alapján a számszerű értékek alapján építjük föl, amely értékeket az indikátor tömbök tartalmaznak. Egy indikátor tömb pontokkal kapcsolatos információt tartalmaz, olyan koordinátákat, amelyekkel egy indikátorvonalat rajzolunk. Az y koordinátán mindegyik pont egy indikátor tömb-elem értéke, és az X koordinátát az indikátor tömbelem-indexe határozza meg. A vizsgált példában az első indikátorvonalat a bárok legnagyobb értékei alapján rajzoltuk. A 116. ábrán látható az az indikátorvonal (kék színű) a szimbólum ablakban, ami a Buf_0 indikátor tömb alapján hoztunk létre.


A Buf_0 indikátor tömb index értéke A Buf_0 indikátor tömb elemértéke
0 1.3123
1 1.3124
2 1.3121
3 1.3121
4 1.3123
5 1.3125
6 1.3127
... ...

116. ábra. Egy indikátor vonal koordinátáinak az összefüggése egy indikátor tömb értékeivel.

Egy indikátor tömb index értéke és az ügyfélterminál bárindex értéke összefügg, ezek az indexek egyenlők. Azt szintén figyelembe kell venni, hogy az indikátor vonalak megjelenítése válós idejű módban történik, mialatt a szimbólum ablakban újabb és újabb bárok jelennek meg. Ilyenkor minden történelmi bárt elmozdul balra. A helyesen rajzolt indikátorvonalak, (a vonal pontjai a bárok fölött vannak) a bárokkal együtt szintén elmozdulnak. Tehát szükség van (technikailag) az indikátortömb újra indexelésére.

Az alapvető különbsége egy szokásos tömb és egy indikátortömb között a következő:

Figyelem!

Abban a pillanatban, amikor egy új bár kialakul, az indikátor tömb-elemek index értékeit meg változtatja az ügyfélterminál, mégpedig automatikusan - mindegyik indikátor tömb-elem index értékét eggyel növeljük és az indikátortömb méretét növeljük egy elemmel (a null indexűvel).

Például, a null bár a116. ábrán (H1 idő keret) nyitási ideje 6:00. 7:00-kor egy új bár fog megjelenni az ablakban. Az a bár, amit 6:00-kor kinyitottunk, automatikusan az 1. indexet fogja kapni. Hogy az indikátor vonal helyesen legyen rajzolva ezen a báron, az ügyfélterminál meg fogja változtatni az indikátor tömb-elem indexét a 6:00-kor nyitott bár indexére. A116. ábra táblázatában ezt az elemet írtuk az első sorba. Ezzel együtt az ügyfélterminál minden tömbelem indexet növelni fog egyel. Annak a tömbelemnek az indexe, ami a 6:00-kor nyitott bárhoz tartozik, fogja kapni az 1. értéket (azelőtt ez 0 volt). Az indikátortömb egy új elemet fog tartalmazni. A új, hozzáadott elem indexe egyenlő lesz 0-val, ennek az elemnek az értéke egy új érték lesz ami visszatükrözi a null báron lévő indikátor vonal koordinátáját. Ezt az értéket a start() különleges függvényben minden ticknél kiszámoljuk .

A számításokat a start() különleges függvényben úgy kell elvégezni, hogy fölösleges (extra) műveleteket ne hajtsunk végre. Mielőtt az indikátort hozzá csatoltuk egy ábrához, ez nem tükröz vissza semmilyen indikátor vonalat (mert az indikátor vonalak értékeit még nem számoltuk ki). Ezért a start() különleges függvény első végrehajtásánál az indikátortömb értékeket ki kell számolni minden olyan bárra, amelyekre az indikátor vonalat rajzolni kell. A vizsgált példában ezt a számítást minden, az ábrán jelenlévő báron elvégezzük (a kezdeti számításokat nem minden elérhető báron kell elvégezni, csak a történelemi bárok meghatározott utolsó tartományán; ezt később példákkal alátámasztjuk). A start()különleges függvény későbbi indításakor már nem szükséges megint kiszámítani az indikátortömb értékeit minden bárra. Ezeket az értékeket már kiszámoltuk és megtalálhatók az indikátortömbben. Csak a null báron kell kiszámítani az új tick érkezésekor az indikátor vonal (árfolyam) értékét.

A fentebb leírt technológia megvalósítására létezik egy nagyon hasznos beépített MQL4 függvény az IndicatorCounted() .

IndicatorCounted() függvény

int IndicatorCounted()

Ez a függvény visszaküldi azon bárok számát, amelyek nem változtak az utolsó indikátorhívás óta.

Ha az indikátort még soha nem csatoltuk hozzá egy charthoz, akkor a start() első végrehajtásnál a Counted_bars le fog nullázódni:

 Counted_bars=IndicatorCounted();  // Number of counted bars

Ez azt jelenti, hogy az indikátortömb nem tartalmaz semmilyen korábban kiszámolt elemet, ezért az egész indikátor tömböt ki kell számolni az elejétől a végéig. Az indikátortömböt a legrégebbi bártól kezdve a nulla bár felé haladva számoljuk ki. A legrégebbi bárt, amelyen a számítást el kell végezni a következő módon határozzuk meg:

 i=Bars-Counted_bars-1; // Index of the first uncounted

Tegyük fel, hogy a pillanatnyilag csatolt az indikátor chart ablakában 300 bár van. Ez az előre definiált Bars változó értéke. Ahogy korábban meghatároztuk a Counted_bars egyenlő 0-val. Tehát megkaptuk az első kiszámolandó bár indexét (ahol a számításokat kell kezdeni) ami egyenlő 299-cel.

Az indikátortömb minden elem értékét egy while() ciklusban számoljuk ki:

 while(i>=0) // Loop for uncounted bars
 {
 Buf_0[i] = High[i]; // Value of 0 buffer on i bar
 Buf_1[i] = Low[i]; // Value of 1st buffer on i bar
 i--; // Calculating index of the next bar
 }

Amíg i az első kiszámolandó bártól (299) az aktuális bárig (0) terjedő tartományon belül van, az indikátor tömbelemek értékeit kiszámoljuk mindkét indikátorvonalhoz. Megjegyzés: az indikátor tömbelemek hiányzó értékeit egyszer számoljuk ki a különleges start() függvény első végrehajtásakor. A számítások alatt az ügyfélterminál azokra az elemekre emlékszik, amelyek értékei már ki voltak számolva. Az utolsó 'while' ismétlést akkor hajtjuk végre mikor i egyenlő 0-val, ekkor az indikátortömb értékét kiszámoltuk a null báron is. Amikor a 'while' ciklusnak vége van, a különleges start() függvény végrehajtása befejeződik és a vezérlést megkapja az ügyfélterminál. Az ügyfélterminál ezután fel fog rajzolni minden (ebben az esetben kettő) indikátorvonalat a tömbelemek kiszámított értékei alapján.

A következő ticknél a start() függvényt megint el fogja indítani az ügyfélterminál. A további események az aktuális helyzettől fognak függeni (például továbbra is elemezni fogunk 300 bárt).

1.variáció. Az új tick az aktuális nulla bár képződése alatt jön (ez a leggyakoribb helyzet).


117. ábra. A feldolgozott tick az aktuális bárhoz tartozik.

A 117. ábrán két ticket láthatunk, amit a terminál a t 1 és t 2 idő pillanatban kapott. A vizsgált helyzet ugyanaz lesz mindkét tick esetén. Elemezzük a t 1 pillanatban elindított start() végrehajtását. A start() függvény először a következő sort fogja végrehajtani:

 Counted_bars=IndicatorCounted(); // number of counted bars

Az IndicatorCounted() függvény a 299 értéket fogja visszaküldeni az, mert az utolsó start() hívás óta 299 változatlan bárt talált. Ennek következtében az index értéke egyenlő lesz 0-val (300-299-1):

 i=Bars-Counted_bars-1; // Index of the first uncounted

Ezek szerint a következő while() hurokban csak a null indexszel rendelkező tömbelemek értékeit kell kiszámolni. Más szóval a null báron lévő indikátor vonal új pozícióját számoljuk ki. Amikor a ciklus kész, a start() véget ér és a vezérlés visszakerül az ügyfélterminálhoz.

2. variáció. A az új tick egy null bár első tickje (időről-időre előfordul).


118. ábra. A feldolgozott tick egy új nulla bár első tickje.

Ebben az esetben egy új bár megjelenése fontos tényező. Először a vezérlés a különleges start() függvénybe kerül, az ügyfélterminál újra fogja rajzolni a bárokat a szimbólumablakban és újra indexel minden indikátor tömböt (a bufferekkel összhangban). Azonkívül, az ügyfélterminál felismeri, hogy már 301 bár van az ablakban és nem 300.

118. ábrán az a helyzet látható, amikor az előző bár utolsó tickje hatására (t 2 pillanatban) a start() függvény sikeresen elindult és lefutott. Ezért, mivel most az előző bár már bezárult (1. indexszel), t 2 pillanatban kiszámoltuk az indikátor értékét, az IndicatorCounted() függvény visszaadja azt az érték, ami az előző báron volt, ami 299:

 Counted_bars=IndicatorCounted(); // Number of counted bars

A következő sorban az i indexet számoljuk ki, ami az új bár első tickje esetén 1 lesz (301-299-1):

 i=Bars-Counted_bars-1; // Index of the first uncounted

Ez azt jelenti, hogy az indikátor tömb elem értékének kiszámításához az új bár megjelenését követően a while() ciklust kétszer kell végrehajtani, a kialakult báron és az új null báron. Korábban az indikátortömb újra indexálásakor az ügyfélterminál növelte a tömbök a méretét. A nulla indexel rendelkező tömbelemek értékeit nem ismertük a while() ciklus végrehajtása előtt. A ciklus végrehajtása során ezek az elemek is kapnak értéket. Amikor számításoknak a start() függvényben vége van, a vezérlés vissza kerül az ügyfélterminálhoz. Azután az ügyfélterminál az indikátor vonalat úgy fogja megrajzolni, hogy a nullaindexű tömbelem értékeként a legutoljára kiszámított értékeket fogja használni.

3. variáció. Az új tick egy új nulla bár első tickje, de az előző báron az utolsó tick nincs feldolgozva (ritkán előfordul).


119. ábra. Nincs az előző báron minden tick feldolgozva.

A 119. ábrán azt az esetet láthatjuk mikor a start() függvényt az új bár első tickjén, t 5 pillanatban indítjuk el. Legutoljára ez a függvény a t 2 időpontban volt elindítva. A t 3 időpontban (piros nyíl) új tick érkezett a terminálhoz, azonban még az indikátor feldolgozása nem ért véget. Ez azért történt mert start() végrehajtási ideje (t 2 - t 4) hosszabb a tickek közötti időköznél (t 2 - t 3). Ezt a tényt észlelni fogja az ügyfélterminál a start() t 5 időpontban indított végrehajtása alatt. A számítás:

 Counted_bars=IndicatorCounted(); // Number of counted bars

Az IndicatorCounted() a 299 (!) értéket fogja visszaküldeni. Ez az érték helyes, mert az utolsó indikátorhívás pillanatában 299 változatlan bárunk volt (most már 301 van). Ezért az első kiszámolandó bár indexe (a bal szélső), ahonnan a tömbelem-értékek számítását indítani kell 1 lesz (301-299-1):

 i=Bars-Counted_bars-1; // Index of the first uncounted

Ezek szerint két 'while' ismétlést fogunk végrehajtani. Az első alatt az i = 1 indexszel rendelkező tömbelemek értékeit számoljuk ki, vagyis Buf_0[1] és Buf_1[1] értékeit. Amikor a számítások kezdődnek, a bárokat és indikátor tömböket már újra indexelte az ügyfélterminál (mert egy új bár kezdődött a különleges start() függvény kezdete előtt). Ezért az 1 indexű tömbök (Buf_0[1], Buf_1[1])  elemértékeit az előre definiált tömbök értékei alapján számoljuk ki (a bár legnagyobb és legkisebb értékét) szintén az 1. indexszel:

 while(i>=0) // Loop for uncounted bars
 {
 Buf_0[i] = High[i]; // Value of 0 buffer on i bar
 Buf_1[i] = Low[i]; // Value of 1st buffer on i bar
 i--; // Calculating index of the next bar
 }

A while()második ismétlése alatt a nulla indexel rendelkező elemek értékeit határozzuk meg, az előre definiált tömböknek a nulla báron utoljára ismert értékei alapján.

Figyelem!

Az ismertetett technológia szerint az egyéni indikátorok értékének meghatározása először a már kiszámolt indikátortömb-elemek értékei alapján történik, és csak a még nem kiszámolt ‘uncounted’ bárokon végzünk tényleges számításokat, ami egy erőforrás takarékos módszer.

Egy bár nem kiszámolt (uncounted), ha az indikátortömb elemértékének a kiszámítását a a bár legutolsó tickje után  még nem hajtották végre.

Az userindicator.mq4 egyéni indikátor elindítása után az ablakban két vonalat fogunk látni - egy vastag kék vonalat a bárok maximumain és egy pontozott piros vonalat a bárok minimum értékein (120. ábra).


120. ábra. Két indikátorvonal a szimbólumablakban, amit az  userindicator.mq4 indikátor hozott létre.

Meg kell jegyezni, hogy tudunk olyan egyéni indikátort is építeni, aminek az indikátorvonalai egybeesnének egy  technikai indikátor vonalaival. Ezt könnyű megvalósítani, ha az egyéni indikátorban levő számítási képletek ugyanazok amelyeket a technikai indikátorban használnak. Ennek szemléltetésére alakítsuk át a programkódot, amit az előző példában elemeztünk! Legyen az indikátor feladata az, hogy rajzolja föl a legutolsó meghatározott számú bár maximumainak és minimumainak átlagos értékeit. Könnyű a szükséges számításokat elvégezni: egyszerűen az előre definiált tömbök elemeinek az átlagos értékeit kell kiszámolni. Például a 3 indexszel rendelkező indikátortömb értéke (az indikátorvonal koordinátája a harmadik báron) az utolsó öt bár maximuma alapján kiszámolva a következő:

Buf_0[3] = ( High[3] + High[4] + High[5] + High[6] + High[7] ) / 5

Hasonló módon számítjuk ki az indikátor vonalat a minimum értékekre.

Példa

Példa egy egyszerű egyéni indikátorra averagevalue.mq4. Indikátor vonalat rajzolunk N bár átlagos minimális és maximális értékein.

//--------------------------------------------------------------------
// averagevalue.mq4 
// The code should be used for educational purpose only.
//--------------------------------------------------------------------
#property indicator_chart_window // Indicator is drawn in the main window
#property indicator_buffers 2 // Number of buffers
#property indicator_color1 Blue // Color of the 1st line
#property indicator_color2 Red // Color of the 2nd line
 
extern int Aver_Bars=5; // number of bars for calculation
 
double Buf_0[],Buf_1[]; // Declaring indicator arrays
//--------------------------------------------------------------------
int init() // Special function init()
 {
//--------------------------------------------------------------------
 SetIndexBuffer(0,Buf_0); // Assigning an array to a buffer
 SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style
//--------------------------------------------------------------------
 SetIndexBuffer(1,Buf_1); // Assigning an array to a buffer
 SetIndexStyle (1,DRAW_LINE,STYLE_DOT,1);// Line style
//--------------------------------------------------------------------
 return; // Exit the special funct.init()
 }
//--------------------------------------------------------------------
int start() // Special function start()
 {
 int i, // Bar index
 n, // Formal parameter
 Counted_bars; // Number of counted bars
 double
 Sum_H, // Sum of High values for period
 Sum_L; // Sum of Low values for period
//--------------------------------------------------------------------
 Counted_bars=IndicatorCounted(); // Number of counted bars
 i=Bars-Counted_bars-1; // Index of the first uncounted
 while(i>=0) // Loop for uncounted bars
 {
 Sum_H=0; // Nulling at loop beginning
 Sum_L=0; // Nulling at loop beginning
 for(n=i;n<=i+Aver_Bars-1;n++) // Loop of summing values
 {
 Sum_H=Sum_H + High[n]; // Accumulating maximal values sum
 Sum_L=Sum_L + Low[n]; // Accumulating minimal values sum
 }
 Buf_0[i]=Sum_H/Aver_Bars; // Value of 0 buffer on i bar
 Buf_1[i]=Sum_L/Aver_Bars; // Value of 1st buffer on i bar
 
 i--; // Calculating index of the next bar
 }
//--------------------------------------------------------------------
 return; // Exit the special funct. start()
 }
//--------------------------------------------------------------------

Ebben a példában van egy külső változó: Aver_Bars. Ennek a változónak a segítségével tudja a felhasználó meghatározni azon bárok számát, amelyeknek az átlagos értékét kiszámoljuk. A start() függvényben ezt az értéket használjuk a számításban résztvevő bárok számának meghatározására. A 'for' hurokban az Aver_Bars változóban meghatározott számú bár maximális és minimális értékeinek az összegét számoljuk ki. A következő két programsorban az indikátortömb elemek értékeit számoljuk ki az indikátorvonalak minimális és maximális értékére.

Az itt használt átlagoló módszert alkalmazzák a Moving Average technikai indikátorban. Ha hozzácsatoljuk a charthoz az averagevalue.mq4 egyéni indikátort és a Moving Average technikai indikátort, három indikátorvonalat fogunk látni. Ha ugyanazokat a paramétereket használjuk mindkét indikátornak, a Moving Average indikátor vonala az egyéni indikátorvonalak közül az egyikkel egybe fog esni ( a 121. ábra szerint kell a technikai indikátor beállításokat elvégezni).

121. ábra. Egy technikai indikátor és egy egyéni indikátor egymást átfedő vonalai (narancssárga vonal).

Így a technikai indikátorok segítségével a felhasználó a gyakorlatban bármilyen szabályosság visszatükröződését meg tudja jeleníteni.

Egyéni indikátor opciók

Az indikátor vonalak rajzolása különálló ablakban

Az MQL4 egy nagyon kényelmes szolgáltatást biztosít az egyéni indikátorok használatához. Különböző indikátorokat helyezhetünk különálló ablakokba. Ez különösen akkor kényelmes, amikor az indikátor vonalak abszolút értékeinek amplitúdója lényegesen kisebb (vagy nagyobb) a szimbólum áraknál. Például, ha a bárok átlagos maximum és minimum értékei közti különbségre vagyunk kíváncsiak egy bizonyos egy időintervallumon belül, az időkerettől függően ez az érték hozzávetőleg 0- 50 pont lesz (például: M15). Nem nehéz a vonalakat felrajzolni az indikátornak, de a szimbólum ablakban ez a vonal a 0 - 50 pont tartományban fog elhelyezkedni a szimbólumablak legalján és ez jelentősen torzítja az szimbólum grafikonját a képernyőn. Ez nagyon kellemetlen.

Az indikátorvonal különálló ablakban történő elhelyezéséhez (amelyik a szimbólum ablak alsó részében van) a #property utasításban (a program kezdetén) az indicator_separate_window paramétert kell megadni:

#property indicator_separate_window // Indicator is drawn in a separate window

Amikor egy ilyen indikátort csatolunk egy ablakhoz, ügyfélterminál létrehoz egy különálló ablakot a szimbólum ábra alatt, amiben az indikátorban kiszámolt vonalakat rajzolni fogja. Itt a színbeállításoktól és a vonal stílustól függően fognak megjelenni az indikátorvonalak.

A számítás előtörténetének korlátozása

A legtöbb esetben az indikátorvonalaknak csak a legutóbbi része tartalmaz hasznos információt. Az indikátorvonalaknak az a része, amelyeket régebbi bárok alapján számoltunk, (például 1 hónapnál régebbi alacsony időkeret) aligha lehet hasznos a kereskedelmi döntések meghozatalakor. Azonkívül, ha sok bár van egy ablakban, az idő, ami az indikátor vonalak számításához és rajzolásához szükséges, ésszerűtlenül nagy. Ez a program hibaelhárítása során lehet kritikus, amikor egy programot gyakran fordítanak és indítanak el. Ezért a számításokat nem végezzük el az összes elérhető báron, hanem csak a bárok legutóbbi korlátozott részén.

Erre a célra a külső History változót használjuk a következő programban. Ennek a változónak az értékét figyelembe vesszük az első számítandó bár (bal szélső) indexének meghatározásakor, ahonnan az indikátortömbök elemértékeinek kiszámítását kezdjük.

 i=Bars-Counted_bars-1; // Index of the first uncounted
 if (i>History-1) // If there are too many bars...
 i=History-1; // ..calculate for specified amount.

A további számításokat a while() hurokban fogjuk elvégezni a History értékénél nem nagyobb számú történelmi báron. Megjegyzés: az ismertetett módszer, a számítás történelmi mélységének korlátozására csak az olyan számításokat érinti, amelyeket a start() különleges függvény első kezdésekor végeznek. Később, amikor az új bárok megjelennek, az indikátorvonalhoz a jobb oldalon az új szakaszok hozzá fognak adódni, és a bal oldali része is megmarad. Így az indikátorvonal hossza az indikátor működése alatt idővel növekedni fog. A History paraméter szokásos értéke hozzávetőleg 5000 bár.


Példa

Példa egy egyszerű egyéni indikátorra,  separatewindow.mq4. Az indikátorvonalak egy különálló ablakban jelennek meg.

//--------------------------------------------------------------------
// separatewindow.mq4 
// The code should be used for educational purpose only.
//--------------------------------------------------------------------
#property indicator_separate_window // Drawing in a separate window
#property indicator_buffers 1 // Number of buffers
#property indicator_color1 Blue // Color of the 1st line
#property indicator_color2 Red // Color of the 2nd line
 
extern int History =50; // Amount of bars in calculation history
extern int Aver_Bars=5; // Amount of bars for calculation
 
double Buf_0[]; // Declaring an indicator array
//--------------------------------------------------------------------
int init() // Special function init()
 {
 SetIndexBuffer(0,Buf_0); // Assigning an array to a buffer
 SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// line style
 return; // Exit the special funct. init()
 }
//--------------------------------------------------------------------
int start() // Special function start()
 {
 int i, // Bar index
 n, // Formal parameter
 Counted_bars; // Number of counted bars
 double
 Sum_H, // Sim of High values for period
 Sum_L; // Sum of low values for period
//--------------------------------------------------------------------
 Counted_bars=IndicatorCounted(); // Number of counted bars
 i=Bars-Counted_bars-1; // Index of the first uncounted
 if (i>History-1) // If too many bars ..
 i=History-1; // ..calculate for specific amount.
 while(i>=0) // Loop for uncounted bars
 {
 Sum_H=0; // Nulling at loop beginning
 Sum_L=0; // Nulling at loop beginning
 for(n=i;n<=i+Aver_Bars-1;n++) // Loop of summing values 
 {
 Sum_H=Sum_H + High[n]; // Accumulating maximal values sum
 Sum_L=Sum_L + Low[n]; // Accumulating minimal values sum
 }
 Buf_0[i]=(Sum_H-Sum_L)/Aver_Bars;// Value of 0 buffer on i bar
 i--; // Calculating index of the next bar
 }
//--------------------------------------------------------------------
 return; // Exit the special funct. start()
 }
//--------------------------------------------------------------------

Ehhez az indikátorhoz hasonló számításokat hajtanak végre az AverageTrue Range technikai indikátorban. 122. ábrán láthatjuk a  separatewindow.mq4 indikátor által létrehozott indikátorvonalat egy különálló ablakban, és egy másik ablakban az ATR által épített indikátorvonalat. Ebben az esetben a vonalak teljesen azonosak mert átlagolási időszak ugyanaz mindkét indikátor esetén: 5. Ha ezt a paramétert megváltoztatjuk bármelyik indikátorban, akkor a megfelelő indikátorvonal szintén változni fog.


122. ábra. Egyéni indikátor vonal rajzolása egy különálló ablakban.
Egy technikai indikátor (ATR), és egy egyéni indikátor azonos vonalai  (separatewindow.mq4).


Szintén nyilvánvaló, hogy az egyéni indikátorvonalat nem az egész képernyőszélességen rajzoltuk, hanem csak az 50 legújabb báron, ahogy azt a History külső változóban beállítottuk. Ha egy kereskedőnek nagyobb történelmi időszakot kell használnia, a külső változó értékét könnyen megváltoztathatjuk az egyéni indikátor beállítási ablakon keresztül.

A 123. ábra egy olyan ablakot mutat, amiben az indikátor vonalat másik stílusban – hisztogramként jelenítettük meg. Ezt az eredményt úgy kaptuk, hogy a  separatewindow.mq4 programkódjában egy sort megváltoztattunk a másik vonalstílus megjelenítéséhez:
 SetIndexStyle (0,DRAW_HISTOGRAM);// Line styl

Minden más kódrész változatlan.


123. ábra. Egyéni indikátorvonal rajzolása egy különálló ablakban (hisztogram).
Egy technikai indikátor (ATR) és egy egyéni indikátor rajzainak hasonlósága,  (separatewindow.mq4).


Indikátor vonalak elmozdítása függőlegesen és vízszintesen

Néhány esetben szükséges az indikátorvonalak elmozdítása. Ezt MQL4 eszközökkel könnyen megtehetjük. Nézzünk egy példát arra, hogy az indikátorvonal pozícióját elmozdítottuk a felhasználó által beállított értéknek megfelelően.

Példa

Példa egy egyéni indikátorra displacement.mq4. Az indikátorvonalat elmozdítottuk vízszintesen és függőlegesen.

//--------------------------------------------------------------------
// displacement.mq4 
// The code should be used for educational purpose only.
//--------------------------------------------------------------------
#property indicator_chart_window //Indicator is drawn in the main window
#property indicator_buffers 3 // Number of buffers
#property indicator_color1 Red // Color of the 1st line
#property indicator_color2 Blue // Color of the 2nd line
#property indicator_color3 Green // Color of the 3rd line
 
extern int History =500; // Amount of bars in calculation history
extern int Aver_Bars=5; // Amount of bars for calculation
extern int Left_Right= 5; // Horizontal shift (bars)
extern int Up_Down =25; // Vertical shift (points)
 
double Line_0[],Line_1[],Line_2[]; // Declaring data arrays
//--------------------------------------------------------------------
int init() // Special funct. init()
 {
//--------------------------------------------------------------------
 SetIndexBuffer(0,Line_0); // Assigning an array to buffer 0
 SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style
//--------------------------------------------------------------------
 SetIndexBuffer(1,Line_1); // Assigning an array to buffer 1
 SetIndexStyle (1,DRAW_LINE,STYLE_DOT,1);// Line style
//--------------------------------------------------------------------
 SetIndexBuffer(2,Line_2); // Assigning an array to buffer 2
 SetIndexStyle (2,DRAW_LINE,STYLE_DOT,1);// Line style
//--------------------------------------------------------------------
 return; // Exit the special funct. init()
 }
//--------------------------------------------------------------------
int start() // Special function start()
 {
 int i, // Bar index
 n, // Formal parameter (index)
 k, // Index of indicator array element
 Counted_bars; // Number of counted bars
 double
 Sum; // High and Low sum for the period
//--------------------------------------------------------------------
 Counted_bars=IndicatorCounted(); // Number of counted bars
 i=Bars-Counted_bars-1; // Index of the 1st uncounted
 if (i>History-1) // If too many bars ..
 i=History-1; // ..calculate for specified amount.
 
 while(i>=0) // Loop for uncounted bars
 {
 Sum=0; // Nulling at loop beginning
 for(n=i;n<=i+Aver_Bars-1;n++) // Loop of summing values 
 Sum=Sum + High[n]+Low[n]; // Accumulating maximal values sum
 k=i+Left_Right; // Obtaining calculation index
 Line_0[k]= Sum/2/Aver_Bars; // Value of 0 buffer on k bar
 Line_1[k]= Line_0[k]+Up_Down*Point;// Value of the 1st buffer
 Line_2[k]= Line_0[k]-Up_Down*Point;// Value of the 2nd buffer
 
 i--; // Calculating index of the next bar
 }
//--------------------------------------------------------------------
 return; // Exit the special funct. start()
 }
//--------------------------------------------------------------------

Az indikátorvonalak elmozdítására két külső változót használunk, a Left_Right vízszintes irányban és az Up_Down függőleges irányban tolja el a két pontozott vonalat.

extern int Left_Right= 5;  // Horizontal shift (bars)
extern int Up_Down = 25;  // Vertical shift (points)

A tömbelemek értékeit kiszámító algoritmus nagyon egyszerű szabályokon alapul:

  • az indikátor vonal vízszintes eltolásához, a tömbelem indexeket a Left_Right változó értékével módosítani kell azon bárok indexeihez képest ahol a tömbelemek értékeit kiszámoltuk. (Ha az indexek közti különbség növekszik, akkor az indikátorvonal balra tolódik, ha csökken, akkor jobbra.)
  • ha függőlegesen akarunk elmozdítani egy vonalat, az Up_Down*Point kifejezés értékét hozzá kell adni (vagy ki kell vonni) egy indikátortömb mindegyik számított értékéhez;

A vizsgált példában az indexet meghatározó sor:

 k = i+Left_Right;  // Obtaining calculation index

Itt i a bár indexe, amin a számításokat végrehajtottuk, k egy indikátortömb-elem indexe. A piros indikátorvonalat az ügyfélterminál a Line_0[] indikátortömb alapján 5 bárral balra elmozdítva (a 124. ábrán látható beállítások szerint) rajzolja a kiinduló vonaltól. Ebben az esetben a kiinduló Moving Average vonal átlagolt időszaka egyenlő 5-tel; az MA számítás képlete (High[i]+Low[i])/2.

 Line_0[k]= Sum2 Aver_Bars;  // Value of 0 buffer on k bar

Ebben a példában a piros vonal pozíciója a két másik vonal indikátortömb-értékeinek a számítási alapja, ettől függ a pozíciójuk az ábrán. A pontozott vonalak számított értékek:

 Line_1[k]= Line_0[k]+Up_Down*Point;// Value of the 1st buffer 
 Line_2[k]= Line_0[k]-Up_Down*Point;// Value of the 2nd buffer

A k index használata lehetővé teszi, hogy a Line_1[], Line_2[] indikátortömb elemeit az Line_0[] elemeihez hasonló módon számítsuk ki. Ennek következtében a pontozott vonalakat elmozdítottuk a piros vonalhoz viszonyítva 30 ponttal, amit az indikátor beállítási ablakban, határoztunk meg (124. ábra).


124. ábra. A piros indikátorvonalat eltoltuk balra 5 bárral.
A pontozott indikátorvonalakat 30 ponttal elmozdítottuk a piros vonalhoz viszonyítva.

Az egyéni indikátorok korlátai

Van néhány olyan korlátozás, amit figyelembe kell venni az MQL4 egyéni indikátorok programozása során.

Vannak olyan függvények, amiket csak egyéni indikátorokban használhatunk, és nem használhatjuk Expert Advisorsban és scriptben: IndicatorBuffers(), IndicatorCounted (), IndicatorDigits(), IndicatorShortName(), SetIndexArrow(), SetIndexBuffer(), SetIndexDrawBegin(), SetIndexEmptyValue(), SetIndexLabel(), SetIndexShift(), SetIndexStyle(), SetLevelStyle(), SetLevelValue().

Másfelől kereskedelmi függvényeket nem használhatunk indikátorokban: OrderSend(), OrderClose(), OrderCloseBy(), OrderDelete() és OrderModify(). Ez azért van, mert az indikátorok az interfész környezetben működnek (ezzel szemben az Expert Advisorok és a scriptek a sajátjukban).

Ez az oka annak is, hogy nem lehet hurkolódó algoritmusokat használni az egyéni indikátorokban. Ha elindítunk egy egyéni indikátort, ami egy végtelen ciklust tartalmaz (a tényleges végrehajtási idő tekintetében) az az ügyfélterminál lefagyásával végződik, ami a számítógép újraindítását teszi szükségessé.

Az Expert Advisorok scriptek és indikátorok általános jellemzőit a  2. táblázat tartalmazza.