MQL4 könyv    Egy szokásos program létrehozása   Eseménykövető függvény

Eseménykövető függvény

Sok esemény történik a kereskedés alatt. A kereskedő néhányat, például a piaci ár-változását vagy indikátorvonalak kereszteződését közvetlenül láthat a szimbólumablakban. Más események, bár szintén fontosak a kereskedő számára nyíltan sehol nem láthatók. Ezeknek az eseményeknek a jelentős részét észlelhetjük és feldolgozhatjuk az MQL4 segítségével.

Például a dealing center megváltoztathatja a kereskedelmi feltételeket a fontos hírek előtt, vagy mikor a piac nagyon aktívvá válik. Ilyen esetben a spread vagy a stop megbízások elhelyezésének minimális megengedett távolsága megnövekedhet. Ha ez történik, először is figyelembe kell venni az új kereskedési feltételek meghatározásánál, és másodszor ezekről a változásokról tájékozatni kell a kereskedőt.

Ezen feladatok megoldására használhatjuk az eseménykövető függvényt az Expert Advisorban.

Az Events() felhasználói függvény

int Events()

A függvény kiszámítja a stopmegbízások megadásának minimális távolságban, – valamint a rendelkezésre álló piaci és függőben levő megbízások listájában bekövetkező változásokat. A függvény végrehajtásához használnunk kell a Terminal() megbízás nyilvántartó függvényt is a programban. A következő globális tömbök értékeit használjuk:

  • Mas_Ord_New - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a Terminal() függvény végrehajtásának pillanatában;
  • Mas_Ord_Old - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a Terminal() függvény előző végrehajtásának pillanatában;

A következő globális változók értékeit használjuk:
- Level_new - a minimális távolság értéke;
- Level_old - a minimális távolság előző értéke.

Az üzeneteket megjelenítésére az Inform() adatfüggvényt fogjuk használni. Ha az Inform() függvény nincs beillesztve az Expert Advisorba, az üzenetek nem fognak megjelenni.

Az Events() eseménykövető függvényt az  Events.mqh include fájl tartalmazza:
//--------------------------------------------------------------------------------
// Events.mqh
// The code should be used for educational purpose only.
//--------------------------------------------------------------------------- 1 --
// Event tracking function.
// Global variables:
// Level_new            The new value of the minimum distance
// Level_old            The preceding value of the minimum distance
// Mas_Ord_New[31][9]  The last known array of orders
// Mas_Ord_Old[31][9]  The old array of orders
//--------------------------------------------------------------------------- 2 --
int Events()                              // User-defined function
  {
  bool Conc_Nom_Ord;                    // Matching orders in ..
  //.. the old and the new arrays
//--------------------------------------------------------------------------- 3 --
  Level_new=MarketInfo(Symbol(),MODE_STOPLEVEL );// Last known
  if (Level_old!=Level_new)              // New is not the same as old..
    {                                    // it means the condition have been changed
      Level_old=Level_new;                // New "old value"
      Inform(10,Level_new);              // Message: new distance
    }
//--------------------------------------------------------------------------- 4 --
  // Searching for lost, type-changed, partly closed and reopened orders
  for(int old=1;old<=Mas_Ord_Old[0][0];old++)// In the array of old orders
    {                                    // Assuming the..
      Conc_Nom_Ord=false;                // ..orders don't match
      //--------------------------------------------------------------------- 5 --
      for(int new=1;new<=Mas_Ord_New[0][0];new++)//Cycle for the array ..
        {                                //..of new orders
        //------------------------------------------------------------------ 6 --
        if (Mas_Ord_Old[old][4]==Mas_Ord_New[new][4])// Matched number 
          {                              // Order type becomes ..
            if (Mas_Ord_New[new][6]!=Mas_Ord_Old[old][6])//.. different
              Inform(7,Mas_Ord_New[new][4]);// Message: modified:)
            Conc_Nom_Ord=true;            // The order is found, ..
            break;                        // ..so exiting ..
          }                              // .. the internal cycle
        //------------------------------------------------------------------ 7 --
                                          // Order number does not match
        if (Mas_Ord_Old[old][7]>0 &&    // MagicNumber matches
            Mas_Ord_Old[old][7]==Mas_Ord_New[new][7])//.. with the old one
          {              //it means it is reopened or partly closed
                                            // If volumes match,.. 
            if (Mas_Ord_Old[old][5]==Mas_Ord_New[new][5])
              Inform(8,Mas_Ord_Old[old][4]);// ..it is reopening
            else                            // Otherwise, it was.. 
              Inform(9,Mas_Ord_Old[old][4]);// ..partly closing
            Conc_Nom_Ord=true;              // The order is found, ..
            break;                          // ..so exiting ..
          }                                // .. the internal cycle
        }
      //--------------------------------------------------------------------- 8 --
      if (Conc_Nom_Ord==false)              // If we are here,..
        {                                    // ..it means no order found:(
        if (Mas_Ord_Old[old][6]==0)
            Inform(1, Mas_Ord_Old[old][4]);  // Order Buy closed
        if (Mas_Ord_Old[old][6]==1)
            Inform(2, Mas_Ord_Old[old][4]);  // Order Sell closed
        if (Mas_Ord_Old[old][6]> 1)
            Inform(3, Mas_Ord_Old[old][4]);  // Pending order deleted
        }
    }
//--------------------------------------------------------------------------- 9 --
  // Search for new orders
  for(new=1; new<=Mas_Ord_New[0][0]; new++)// In the array of new orders
    {
      if (Mas_Ord_New[new][8]>0)            //This one is not new, but reopened
        continue;                          //..or partly closed
      Conc_Nom_Ord=false;                  // As long as no matches found
      for(old=1; old<=Mas_Ord_Old[0][0]; old++)// Searching for this order 
        {                                  // ..in the array of old orders
        if (Mas_Ord_New[new][4]==Mas_Ord_Old[old][4])//Matched number..
          {                                          //.. of the order
            Conc_Nom_Ord=true;              // The order is found, ..
            break;                          // ..so exiting ..
          }                                // .. the internal cycle
        }
      if (Conc_Nom_Ord==false)              // If no matches found,..
        {                                  // ..the order is new :)
        if (Mas_Ord_New[new][6]==0)
            Inform(4, Mas_Ord_New[new][4]); // Order Buy opened
        if (Mas_Ord_New[new][6]==1)
            Inform(5, Mas_Ord_New[new][4]); // Order Sell opened
        if (Mas_Ord_New[new][6]> 1)
            Inform(6, Mas_Ord_New[new][4]); // Pending order placed
        }
    }
//-------------------------------------------------------------------------- 10 --
  return;
  }
//-------------------------------------------------------------------------- 11 --

A globális tömböket és változókat, amelyek a függvény végrehajtásához szükségesek, az 1-2 blokkban ismertetjük. A 2-3 blokkban leírt Conc_Nom_Ord helyi változót a kód későbbi részében fogjuk használni a megbízások elemzéséhez.

A függvény követi a megbízások és stop megbízások elhelyezése minimális távolságának változásait. Ezért, a Level_new minimális távolság értékét mindegyik végrehajtása során meghatározza (3-4 blokk) azután összehasonlítja a Level_old értékével (a függvény előző végrehajtása során meglévő érték). Ha ezeknek a változóknak az értékei nem egyelőek egymásnak, ez azt jelenti, hogy a minimális távolságot a függvény utolsó végrehajtása előtt megváltoztatta a dealing center. Ebben az esetben a minimális távolság új értékét kapja a Level_old változó, és az Inform() függvény végrehajtásával megjelenítjük a megfelelő üzenetet.

Általában hasonló módszert használunk más eseményeket észlelésére.  Például a spread változása, a kereskedés engedélyezése az adott szimbólumon, (a MODE_TRADEALLOWED azonosító a MarketInfo() függvényben), az aktuális bár befejezése (lásd:  27. feladat), az indikátorvonalak kereszteződése (lásd:  107. ábra ),  az előre beállított időpont elérkezése stb. A program néhány esemény jellemző értékét felhasználhatja az EA-ban, más eseményekről tájékoztatja a felhasználót.

A 4-10 blokkokban a piaci és függőben levő megbízások állapotát elemezzük. A megbízások állapotának megváltozásával kapcsolatos legtöbb információt megkapja a felhasználó. Az elemzés két szakaszban történik. Az első szakaszban a program azokat a változásokat keresi, amelyek a megszűnt (zárt vagy törölt), típus-módosult vagy részben lezárt (és kisebb méretben visszanyitott) megbízásokkal kapcsolatosak (4-9 blokk). A második szakaszban (9-10 blokk), az új megbízásokat keressük.

A 4-9 blokkban az előző ciklusban meglévő megbízásokat elemezzük a Mas_Ord_Old tömb alapján. A külső ciklusban az ismétlések számát a tömbben lévő megbízások száma határozza meg ( Mas_Ord_Old[0][0] tömbelem). Annak megállapítására, hogy a megbízás létezik-e még az adott pillanatban, meg kell keresni azt a Mas_Ord_New tömbben. Ezt a keresést a belső ciklusban hajtjuk végre (6-8 tömb), a belső ciklusban az ismétlések száma egyenlő a Mas_Ord_New tömb megbízásainak számával (Mas_Ord_New[0][0] tömbelem). A későbbiekben a Mas_Ord_Old tömböt ‘előző tömbnek’, míg a Mas_Ord_New tömböt ’új tömbnek’ fogjuk nevezni.

A 6-8 blokkban a program csak azokat a megbízásokat keresi, amelyek jellemzői különbözőek. Például, a 6-7 blokkban, a megbízás jegyszámát ellenőrizzük (lásd a 4.táblázatban a tömbindex és a megbízásjellemzők kapcsolatát). Ha az előző tömb ellenőrzése során egy megbízás jegyszámát megtaláljuk az új tömbben is, ez azt jelenti, hogy ez a megbízás nem lett bezárva (vagy törölve). Szintén meg kell állapítani, hogy megváltozott-e a megbízás típusa. Ha igen, ez azt jelenti, hogy egy függőben levő megbízásból piaci megbízás lett. Ebben az esetben a megfelelő üzenetet jelenítjük meg az Inform() függvénnyel. Attól függetlenül, hogy a megbízás típusa megváltozott-e vagy sem, ezt a megbízást nem fogjuk tovább elemezni: a program kilép a belső ciklusból és elindítja a külső ciklus egy új ismétlését.

Ha a program a 6-7 blokkban végrehajtott ellenőrzés során az előző tömbben meglévő jegyszámot az új tömbben nem találja, a vezérlést átadja a 7-8 blokkba. Itt a program megállapítja, hogy az aktuális megbízás MagicNumbere nem nulla (minden nyitott piaci, és elhelyezett függőben lévő megbízásnak az EA adhat egy nem nulla MagicNumbert). Ha a megbízásnak van MagicNumbere és ez a paraméter azonos egy, az új tömbben lévő megbízás MagicNumberjével, ez azt jelenti, hogy ez a megbízás létezik, de a jegyszáma valahogy megváltozott. Két lehetőség van, amikor a jegyszám megváltozhat.

1. lehetőség. A megbízás egy része lett bezárva. Egy piaci megbízást (nem egy függőben levőt!) az MT 4 technológia szerint kétféle képen lehet részlegesen lezárni. Az első módszer az eredeti megbízás teljes lezárása. Ugyanakkor egy kisebb méretű új megbízás nyitása ugyanazzal a nyitó árral és ugyanazokkal a stop szintekkel, mint az eredeti megbízás volt. Ez az új megbízás más azonosítót kap, mint ami az eredeti megbízás azonosítója volt.

2. lehetőség. A megbízást újra kinyitja a dealing center. Valamennyi bróker (a belső könyvelési szabályai miatt) a kereskedési nap végén automatikusan bezár minden megbízást és azonnal kinyitja ugyanazokat a megbízásokat ugyanazzal a típussal és ugyanakkora méretben, de a swap értékét felszámolja. Ez az esemény a swap felszámolásán túl nem hat a kereskedés egyenlegére. Mindegyik újonnan nyitott megbízás a lezárt megbízástól eltérő jegyszámot kap.

A fent két eset közti különbség az új megbízások méretében van: az első esetben a lezárt és újranyitott megbízások különbözőek, és a másodikban változatlanok. Ezt az eltérést használjuk a 7-8 blokkban a megbízások újranyitási okainak megkülönböztetésére. Mindkét esetben a megfelelő üzenetet kapjuk (a megbízás részben lezárt vagy a megbízás újra nyitott).

Ha a program nem észlelte a megbízás meglétét (6-7 blokk) vagy újranyitását (7-8 blokk) az új megbízások tömbjében, akkor a az előző megbízások tömbjében szereplő megbízást lezárták vagy törölték. Ebben az esetben, a vezérlést a 8-9 blokk kapja meg, ahol a megbízás típusa szerinti üzenet megjelenítése történik. A fenti példában három fajta üzenetet hozhatunk létre: egyet-egyet a Buy és Sell megbízásokhoz, és egyet a függőben levő minden típusú megbízáshoz. Ezt a kódot szükség esetén megváltoztathatjuk (kiterjeszthetjük) - létrehozhatunk mindegyik fajta függőben levő megbízásnak megfelelő üzenetet.

A későbbiekben a program az új megbízások tömbjét vizsgálja (9-10 blokk). Ezzel újonnan kinyitott és elhelyezett függő megbízásokat észleli. A külső 'for' ciklusban, a program megvizsgál minden megbízást, ami az új  megbízások tömbjében található. Az újra nyitott vagy részben zárt megbízások azonosítására a program egy egyszerű jellemzőt használ: a megjegyzéseket. Amikor részben bezár vagy újra kinyit egy megbízást, a szerver a megjegyzés mezőben megadja az eredeti megbízás jegyszámát. A fenti EA nem használ megjegyzéseket, ezért a megjegyzés léte azt jelenti, hogy a vizsgált megbízás nem új.

Ha egy megbízás nem tartalmaz megjegyzést, a program ugyanazzal a jegyszámmal keres a megbízások előző tömbjében. Ha a program megtalálja a megbízást a megbízások előző tömbjében, akkor ez azt jelenti, hogy a megbízás nem új, azt régebben nyitották. Mindazonáltal, ha a megbízások új tömbjében található megbízás nem található meg a megbízások előző tömbjében, akkor az a megbízás egy újonnan nyílt piac, vagy újonnan elhelyezett függőben levő megbízás. A 9-10 blokk alsó részében, a program hívja az Inform() függvényt, hogy a megbízás típusának megfelelő üzenetet megjelenítse.

A szóban forgó Events() függvény használata a gyakorlatban nagyon informatív. Ha egyszer a programozó kipróbálja ezt a függvényt egy EA-ban, akkor általában használni fogja a további munkájában is. Külön oda kell figyelni az Events() és Terminal() függvények közötti összefüggésre. Ha ezek közül a függvények közül az egyikben változásokat csinálunk (például, globális tömbök más nevet kapnak), akkor a másikban is végre kell hajtani a megfelelő változtatásokat. Ha a kereskedési stratégia megvalósításában használjuk a megbízásban a megjegyzéseket, akkor másként kell meghatározni az újra nyitott megbízásokat (9-10 blokk), mégpedig string függvényeket kell használni a megjegyzések elemzéséhez.

A valamennyi esemény szemléltetése az Events() függvényben nagyon megnöveli annak méretét. Például ha teljesen be akarunk mutatni mindent, ami a megbízásokkal történik hozzá kell adni a megbízás jellemzők elemzését - a kért stop szinteket, a függőben levő megbízások kért nyitott árait, a megbízások zárásának módszerét (azt, hogy a megbízást egy szemben lévő megbízás nyitásával zárjuk, vagy attól függetlenül) és a megbízás zárásának/törlésének okát (az ár elérte a kért stop szintet vagy a megbízás a kereskedő kezdeményezésére zárult be, stb.).