Programozás MQL4 algoritmikus nyelven

Méret: px
Mutatás kezdődik a ... oldaltól:

Download "Programozás MQL4 algoritmikus nyelven"

Átírás

1 S. Kovalyov Programozás MQL4 algoritmikus nyelven (Fordította: Király János)

2 A könyv fordításáért köszönet Király Jánosnak! A fordítást a saját weboldalán tette közzé:

3 S. Kovalyov Programozás MQL4 algoritmikus nyelven Bevezető Manapság a PC mindenkinek nélkülözhetetlenné vált. Az internet gyors fejlesztése és a modern számítógépek megjelenése az emberi tevékenységek sok területén nyitottak új távlatokat. Még tíz évvel ezelőtt a pénzügyi piac csak bankoknak és a specialisták egy korlátozott közösségének volt elérhető. Ma bárki tud csatlakozni a professzionális kereskedők világához, és bármikor elkezdheti az önálló kereskedést. Az egész világon már több százezer kereskedő ismerte el az elterjedt MetaTrader 4 Client Terminal érdemeit. A beágyazott programnyelvének, az MQL4-nek használata a kereskedőket a kereskedelem egy új szintjére emelte - az automatizált kereskedelembe. Most egy kereskedőprogram alkalmazásával meg tudja valósítani az ötleteit, elképzeléseit - ír egy egyéni indikátort, egy scriptet egyetlen művelet végrehajtására, vagy létrehoz egy Expert Advisort - egy automatizált kereskedő rendszert (kereskedő robotot). Egy Expert Advisor (EA) folyamatosan tud dolgozni bármilyen beavatkozás nélkül biztonsággal követi az árakat, elektronikus üzeneteket küld, és sok más hasznos feladatot végrehajt. Az alkalmazások fő előnye az a lehetőség, hogy a kereskedelem a kereskedő által elképzelt algoritmus szerint alakuljon. Bármilyen ötlet leírható egy algoritmikus nyelven (két mozgó átlag kereszteződése vagy a jelek digitális feldolgozása, Elder vagy Peter' fraktálelemzés, egy neurális hálózat vagy mértani alakzatok) kódolhatók egy alkalmazásban, azután a gyakorlati kereskedelemben tudjuk azokat használni. A MetaTrader 4 Client Terminal alkalmazások fejlesztése az MQL4 nyelv ismeretét igényli. Ez a jelenlegi tankönyv segíteni fog neked a saját Expert Advisorodat, szripjeidet és indikátoraidat létrehozni, hogy megtestesüljenek bennük az ötleteid és az algoritmusaid jövedelmezőek legyenek. A tankönyv a programozással kapcsolatos tapasztalat nélküli olvasóknak készült, akik meg akarják tudni, hogy hogyan lehet fejlődni a MetaTrader 4 Client Terminal automatizálásában. A tankönyvet úgy terveztük, hogy a tanuló kényelmes és következetes módszerrel sajátítsa el az MQL4 nyelvet

4 Előszó Egy fajta nehézséget jelent a kezdőknek írt programozási tankönyvbe fogni, hiszen a vizsgáladó terület néhány olyan új fogalommal rendelkezik, amelyek nem alapulnak semmilyen korábbi ismerten vagy jártasságon. Általánosságban elmondható, hogy ilyen probléma előfordulhat bármely más szakterületen. Például, a pontot végtelenül kicsi körként ismerik a matematikában, míg magát a kört úgy határozzák meg, hogy az bizonyos módon rendeződött pontokból áll. Könnyen látható, ezek a fogalmak átfedik egymást. Ugyanakkor ez az ellentmondás nem jelent akadályt a matematikában. Mind a körök, mind a pontok, valamint az egyéb meghatározások elfogadása a matematikában jól megférnek egymással. Azonfelül mindenki érti, hogy mi egy pont és mi egy kör. Nem nehéz elfogadni, hogy a meghatározások érvényességének a határai bizonytalanok. Azok közül a határok közül néhány annyira homályos, hogy hajlamosak vagyunk kételkedni a tárgy vagy jelenség létezésében, amit a meghatározás leírt. Mindazonáltal az emberi természet eme furcsasága (normális logika tekintetében) nem áll az ember és a gyümölcsöző tevékenysége közé. Miután egy meghatározást bizonyos ideig használunk, annak teljes értelme csak azután nyilvánul meg. Nehéz választ adni a kérdésre, hogy hogyan és miért történik így. De így van. Csak azt tudjuk, hogy a többszörös utalás egy meghatározásra fontos szerepet játszik a tanulási folyamatban. A következő feladatokat fogjuk elvégezni: az új szakkifejezések értelmének kibontása jól ismert analógiákat használva; mindegyik szó jelentésének intuitív kifejtése, amikor az először előfordul; olvasók számára a szükséges mennyiségű információ biztosítása a programokról és a programozásról. Ennek elősegítésére a könyv sok példát és ábrát tartalmaz. A szövegben olyan kereszthivatkozások találhatók, amelyek lehetővé teszik az olvasónak, hogy rokon témákkal kapcsolatos információt kapjon. Néhány szó a szemléltető anyagról. Sok programozási tankönyv a legelső oldalakon felkéri az olvasóit, hogy egy egyszerű programmal írja ki a képernyőre: Hello, World!" A szerzők azt gondolják, amint az olvasóik elkezdenek programozást tanulni, ők egyből írjanak programszövegeket és fokozatosan használják őket, szokják meg, hogy a programok hogyan nézhetnek ki, s ez meg fogja alapozni a később tudásukat. Mindazonáltal ezen megközelítés következtében az olvasó egyszerre több ismeretlen fogalommal találkozik, és csak találgatja a programsorok tartalmát és tulajdonságait. Ez folyamatos félreértéshez vezet, és az olvasó tudása hiányos marad. Ahogy én látom, hatásosabb volna egy olyan módszert használni, ahol az olvasó csak akkor megy a tankönyv következő részére, miután előző anyagokat alaposan elsajátította. Ennek a módszernek az alapján, az első programot csak akkor fogjuk megmutatni az olvasónak, miután elsajátított minden szükséges meghatározást és egy kevés betekintést nyert a kódolás alapvető elveibe. Ajelenlegi tankönyv ezen a módszeren alapul. A könyvben lévő ismeretek elsajátításához szükséges, hogy az olvasó PCfelhasználó legyen és egy kevés tapasztalattal rendelkezzen a MetaTrader 4 -el kapcsolatban ami a MetaQuotes Software Corp terméke

5 Tartalomjegyzék Előszó Bevezetés az MQL4-be Az MQL4 alpjai Néhány alapvető fogalom Állandók és változók Adat tipusok Műveletek és kifejezések Operátorok Függvények Program tipusok MetaEditor Fájlrendszer Programok létrehozása és használata Programozás MQL4-ben Programszerkezet Különleges függvények Program végrehajtás Példák a megvalósításra Operátorok Értékadó operátor 'If-else' feltételes operátor 'while' ciklusoperátor 'For' ciklusoperátor 'Break'operátor 'Continue' operátor 'Switch' operátor Függvényhívás Függvény leírás és a 'return' operátor Változók Előre definiált változók és a RefreshRates függvény A változók fajtái Az ügyfélterminál globális változói Tömbök Gyakorlati programozás MQL4-ben A kereskedelmi műveletek programozása A kereskedés egyszerű módja A megbízások jellemzői és szabályai a kereskedelemben A megbízások elhelyezése Megbízások zárása és törlése, az OrderSelect függvény A megbízások módosítása - 5 -

6 Egyszerű MQL4 programok A technikai indikátorok használata Egyszerű Expert Advisor Az egyéni indikátorok létrehozása ROC (Price Rate of Change) egyéni indikátor A programok kombinált használata Beépített Függvények Általános függvények Grafikus objektumok Chartműveletek String függvény Dátum és ídő Fájlműveletek Tömbök és előre definiált tömbök Matematikai függvények Globális változó függvények Egyéni indikátorok Számlainformáció Kereskdelmi függvények Egy szabályos program létrehozása Egy szabályos program szerkezete Order Accounting Adat függvények Nyomkövetés Mennyiség meghatározó függvény Kereskedelmi feltételek Kereskedő függvények Hibafeldolgozás Az összetett programokról Függelék Szószedet A kereskedelem formái Követelmények és korlátozások a kereskedelemben Hibakódok Az indikátor vonalak stílusai A grafikai objektumok fajtái és tulajdonságai Hangfájlok MessageBox() visszatérési kódok MarketInfo() azonosítók A programok listája - 6 -

7 Bevezetés az MQL4 programozásba Mielőtt elkezdünk MQL4 programozást tanulni, meg fogjuk határozni a tanulmányunk területét. Először is meg kell jegyezni, hogy azokat a programokat, amelyeket ebben a könyvben megvitatunk, csak a MetaTrader 4 Client Terminállal együtt használhatjuk. Lentebb az 1. ábra szemlélteti ezeknek a programoknak a szerepét a kereskedelem menedzselésében. Ezen programok fontosságának a jobb megértéséhez a kereskedelmi menedzsmentben, vessünk egy pillantás az 1. ábrára. 1. ábra. egy MQL4program a MetaTrader 4 Client Terminal részeként. Ha az MQL4 programozás iránt érdeklődsz, meg kellett ismerkedned az ügyfélterminállal. Az ügyfélterminál az online kereskedőrendszer része. Ez a rendszer egy szervert is tartalmaz, ami a dealing centerhez kapcsolódik. A dealing center kapcsolatban áll a többi piaci szereplővel bankokkal és pénzintézetekkel. Az ügyfélterminál egy információs környezettel rendelkezik - biztosít egy sor paramétert, hogy tájékoztatást adjon a piacról, a kereskedő és a dealing center közötti kapcsolatról. Ez az információ tartalmazza az aktuális árat, a legnagyobb és legkisebb kötésméretre vonatkozó korlátozásokat, a stop megbízások minimális távolságát, az automatizált kereskedelem engedélyezését/tilalmát és még sok más hasznos paramétert, amelyek az aktuális állapotot jellemzik. Az információs környezet frissül, amikor új ticket kap a terminál (zöld nyíl az 1. ábrán). Beépített eszközök Az ügyfélterminál olyan beépített eszközöket tartalmaz, amelyek lehetővé teszik a technikai elemzést és kereskedés kézi irányítását. A piac elemezésére technikai indikátorokat és különböző vonalakat használhatunk: támogatás/ellenállás-vonalak, trendcsatornák, Fibonacci szintek, stb. Kézi kereskedésre a megbízások kezelése az eszköztárt használjuk. Miközben a kereskedő használja ezt az eszköztárt, módosíthatja vagy bezárhatja a megbízásokat. Azonkívül a terminálnak van automatizált stop megbízás kezelési opciója is. A kereskedő utasításai a beépített "megbízások kezelése" eszköztár segítségével jutnak el a szerverre. További, az ügyfélterminállal kapcsolatos információért lásd: Userguide (ClientTerminal folder\terminal.chm)

8 Programozható eszközök A piac elemezését és MetaTrader 4 Client Terminal kereskedelmi függvényeinek használatát a programozható eszközök segítségével valósíthatjuk meg. Az MQL4 nyelv lehetővé teszi, hogy ilyen programokat hozzunk létre. Három fajta alkalmazás van, amit MQL4-ben létrehozhatunk, és futtathatunk az ügyfélterminál alatt: egyéni indikátor egy a piaci szabályosságokat grafikusan megjelenítő program, amit a szerző saját algoritmusa szerint írhat meg; Expert Advisor - egy olyan program, ami lehetővé teszi, hogy automatizáljuk a kereskedelem egy részét, vagy egészét; Script - egy olyan program, ami csak egy műveletet végez, beleértve a kereskedelmi megbízások végrehajtását is. Az 1. ábrán láthatjuk, hogy az ügyfél terminál lehetővé teszi, hogy a kézi kereskedelem során használjuk az információs környezetét és beépített eszközeit (a kék nyilak). Ezek beavatkoznak az ügyfélterminál működésébe (a piros nyilak). A különböző típusú programokat egyidejűleg használhatjuk, ezek adatokat cserélnek egymással. Ezen alkalmazások segítségével a programozó automatizálhatja a kereskedelmi műveletek nagy részét vagy alkothat egy olyan robot, ami a kereskedő beavatkozása nélkül fog kereskedni. Az alkalmazásokat és a kézi eszközöket egyidejűleg használhatjuk az ügyfélterminálban, ezek kiegészítik egymást. A kereskedelemnek az az alapvető jellemzője, hogy a MetaTrader online kereskedése során a kereskedést vezérlő utasítások az ügyfélterminálban keletkeznek, és az ügyfélterminál küldi azokat a szerverre. Az alkalmazási programok (Expert Advisor, script, indikátor) csak az ügyfélterminál részeként működnek, ez csatlakozik a szerverhez (dealing center). Semelyik alkalmazási program nem települ a szerverre. A szerver a vezérlő jeleket csak az ügyféltermináltól fogadja el. Ha az ügyfélterminált leválasztják az Internetről vagy egy alkalmazási programról (Expert Advisor vagy script) a vezérlő funkciója megszűnik, semmilyen utasítás nem fog eljutni a szerverre. A tanulmányunk hatálya alá tartozó programok (Expert Advisor, script és egyéni indikátorok) részben vagy egészben lehetővé teszik a kereskedelem automatizálását és jelentősen megnövelik a kereskedelemről kapható információt (lásd az 1. ábrát). Ebben a könyvben meg fogod találni a program összetevőknek leírását és azok alkalmazásának a fő szabályait, hogy létre hozhass és használhass ilyen programokat. Részletesen fogunk foglalkozni a példa programokban az ügyfélterminál információs környezetével is, amely elérhető egy program végrehajtása alatt. Az automatizált kereskedelemmel kapcsolatos programok a kereskedelem kézi eszközeinél sokkal több potenciális lehetőséget biztosítanak. Az esetek többségében a program megkönnyíti a kereskedő munkáját, miközben kiküszöbölik annak a szükségességét, hogy a számítógép előtt ülve folyamatosan figyelje a piaci helyzet. Ez enyhíti az idegfeszültséget és csökkenteni a hibák számát, amik az extrém érzelmi feszültség időszakaiban előfordulnak. De a fő érv a használatuk mellett, hogy lehetővé teszik az ember saját ötleteinek megvalósítását és azok történelmi adatokon való tesztelését, az optimális paramétereket kiválasztani ezekhez az ötletekhez, és végül megvalósítani egy kigondolt kereskedelmi stratégiát

9 Az MQL4 alapjai Ez a fejezet bemutatja az MQL4 programnyelv alapvető fogalmait: Néhány alapfogalom Ilyen fogalom a tick (egy árváltozás), a control' az algoritmusokban és a programokat magyarázó 'comment'. A fő esemény a pénzügyi piacokon az ár változása. Ez az oka annak, hogy a tick egy fontos esemény, ami az MQL4 programok alapvető mechanizmusait működteti. Mit okoz egy új tick érkezése? Milyen események játszódnak le? Ilyenkor a control a vezérlés kerül az előtérbe. De ne felejts el megjegyzéseket fűzni a kódodhoz. Állandók és változók Az állandók és változók fogalmának bemutatása, megmagyarázzuk az ezek közt a fogalmak közti különbséget. Ahogy a meghatározás sugallja, az állandó valami folytonos, egy előre beállított érték. Az állandótól eltérően a változó egy olyan programozható objektum, aminek módosítható a tartalma. Lehetetlen egy programot írni anélkül, hogy megváltoztathatatlan objektumokat használnánk, (állandók) és/vagy az objektumokat a program futása alatt módosítanánk (változók). Adattípusok A programnyelvekben többféle adatot használnak. Egy változó típusát annak célja szerint választják. Hogyan tudunk deklarálni egy változót, hogyan tudjuk inicializálni azt (előre beállítani a kezdőértékét)? Egy a változó típusának hibás megválasztása lelassíthatja a programot vagy hibás működéssel végződhet. Műveletek és kifejezések A műveleteket operandusokon hajtjuk végre. Milyen műveletek vannak? A typecastinget mire használják? Mik az egész számokon végrehajtott műveletek sajátosságai? Miért fontos a különböző adat típusok prioritását meghatározni? A műveletek jellemzőinek ismerete nélkül rejtett hibákat tudsz csinálni. Operátorok Egyszerű és összetett operátorok. Egy szükséges műveletet nem mindig lehet végrehajtani egy egyszerű operátorral. Ha szükséges, akkor egy operátorokból álló csoportot egy nagy operátorként használunk, ezt a csoportot egyetlen összetett operátorként kezeljük. Ismertetjük a követelményeket és speciális példákat adunk. Függvények Annak a szükségletnek a felismerésétől, hogy könnyen kezelhető kódot kapjunk, eljutunk függvény fogalmáig. Azért hogy a programban különböző helyeken használhassuk a függvényeket, paraméterezni kell őket. Az függvények létrehozását fogjuk ismertetni. Példákat mutatunk a beépített függvények használatára. Program típusok Scriptek, indikátorok és Expert Advisor, ezek azok az MQL4 programok, amelyek a gyakorlatban segítenek a kereskedelem során felmerült problémák megoldásában. Tudni kell, hogy a rendelkezésre álló programokat miként lehet a legmegfelelőbb módon használni a MetaTrader 4 Client Terminalban

10 Néhány alapfogalom Az érdeklődésünk tárgya programok írása MQL4 nyelven. Mielőtt elkezdjük a programírás szabályainak részletes bemutatását, szükséges, hogy leírjuk azokat az alapvető fogalmakat és összefüggéseket, amik egy programot jellemeznek. Közismert, hogy a MetaTrader 4 ügyfél terminál online üzemmódban dolgozik. A helyzet a pénzügyi piacokon folytonosan változik, ez a változás hatással van chartokra az ügyfélterminálban. A tick-ek látják el az ügyfélterminált a piaci árváltozásokkal kapcsolatos információval. A tick fogalma A tick egy olyan esemény, ami egy szimbólumot (instrumentum) jellemez a új ár megjelenésének a pillanatában. A tickeket minden ügyfélterminálnak egy szerver kézbesít, ami a dealing centerrel áll kapcsolatban. Az aktuális piaci helyzetnek megfelelően többé vagy kevésbé gyakran kapjuk a tickeket, de minden esetben amikor az adott devizapár árfolyama megváltozik új tick keletkezik. Egy alkalmazás az ügyfélterminálban hosszú időszakon keresztül működhet, akár több napon vagy héten keresztül is futhat. Mindegyik alkalmazás azok szerint a szabályok szerint fut, amely szabályokat annak a bizonyos típus programnak meghatároztak. Például, egy Expert Advisor (EA) nem működik folyamatosan. Egy Expert Advisort általában csak abban a pillanatban indít el az ügyfélterminál, amikor egy új tick jön. Az Expert Advisor működésének az ideje attól függ, hogy milyen programkódot tartalmaz. A normal EA valamennyi információ feldolgozási ciklust tized vagy század másodpercek alatt elvégez. Ezen az időszakon belül az EA végrehajthatott néhány utasítást, egy kereskedelmi döntést hozhatott, elláthatja a kereskedőt egy hasznos információval, stb. Ha az EA befejezte a munkájának ezt a részét várakozik, amíg egy új tick jön. Ez az új tick megint elindítja az Expert Advisort, a program megint végrehajtja a megfelelő műveleteket és visszatér a várakozó üzemódba. Annak a részletes leírását, hogy egy új tick megjelenése hogyan befolyásolja a program futását később tárgyaljuk. A control (vezérlés) fogalma Miközben a kód végrehajtás folyamatáról beszélünk programnak az ügyfélterminállal való együttműködése során, használni fogjuk a control (vezérlés) fogalmát. A vezérlés folyamata a programba előre bekódolt algoritmus végrehajtása és az ügyfélterminál szolgáltatásainak használata. A vezérlés átvihető a programon belül egy kódrészből egy másikba, és a programból az ügyfélterminálra. A vezérlés működése bizonyos szempontból hasonló ahhoz mikor valaki egy gyűlésen felszóllal. Mint szónok néha szót kap majd átadja a szót másoknak, az ügyfélterminál és a program kölcsönösen adogatják a vezérlést egymásnak. Az ügyfélterminál uralkodik. A státusza magasabb, mint a programnak, mint ahogy egy találkozó elnökének a jogköre nagyobb, mint az egyszerű beszélőé. Mielőtt a programot elindítjuk, a control (vezérlés) az ügyfélterminál felügyelete alatt van. Amikor egy új tick megjelenik, az ügyfélterminál átadja az irányítást a programnak. A programkód ebben a pillanatban végrehajtódik. Az ügyfélterminál, miután átadta az irányítást a programnak, nem állítja le a működését. Továbbra is működik maximális teljesítménnyel egész addig, míg a PC-n futtatják. A program csak pillanatnyilag működik, amikor az ügyfélterminál átadta neki a vezérlést (mint amikor egy gyűlés elnöke egész idő alatt folyamatosan irányítja a találkozót, míg az aktuális beszélő csak korlátozott időszakban kap szót). Miután a program befejezte a műveletet, az irányítást visszaadja az ügyfélterminálnak és nem indítja el újra saját magát. Mindazonáltal, amikor az irányítást már átvette a program, ez maga adja vissza az irányítést az ügyfélterminálnak. Más szóval az ügyfélterminál nem tudja visszavenni az irányítást a programtól. A felhasználó beavatkozása (például, a program bezárása) ezalól kivételt képez. Miközben programok belső szerkezetét és működését tárgyaljuk, az érdeklődésünk elsősorban a vezérlés programon belüli működése felé irányul. Figyeljük meg a 2. ábrát, ez megmutatja a vezérlés átadását egy

11 programon belül. Az ábrán a körök a program kicsi, logikailag összefüggő töredékeit jelentik, míg a körök között a nyilak azt mutatják, hogy a vezérlés hogyan kerül át egy töredékről egy másikba. 2. ábra. A vezérlés átadása egy programon belül Egy olyan program, ami megkapta a vezérlést az ügyféltermináltól, (a futó program) végrehajtja az algoritmusa szerinti műveleteket. A program programsorokból áll, a programvégrehajtás folyamata a vezérlés sorról sorra felülről lefelé történő átadásából áll. Hogy milyen szabályok szerint írjuk ezeket a programsorokat azt a későbbiekben részletesenfogjuk tárgyalni. Itt fontos hangsúlyozni, hogy minden logikailag egybefüggő programrész végrehajtódik, például egy matematikai számítás elvégzése, egy üzenet kiírása a képernyőre, egy kereskedelmi megbízás, stb. Amíg a program aktuális része végrehajtódik, a program magánál tartja a vezérlést. Miután az aktuális rész végrehajtását teljesen befejezte, a vezérlést áthelyezi egy másik kódrészletre. Így a vezérlés egy programon belül átkerül az egyik logikailag összefüggő kódrészletből egy másikba, hogy azt is végrehajtsa. Amint az utolsó kódrészt is végrehajtotta a program a vezérlés vissza fog szállni (return) az ügyfélterminálra. A comment (komment) fogalma Egy program két fajta adatsorból áll: a programkódból és a programot magyarázó szövegekből. Comment a programnak egy szabadon választható nem futtatható része. Tehát a Comment egy programnak szabadon választható része. Ez azt jelenti, hogy egy kész program a kódja szerint fog működni függetlenül attól, hogy megjegyzések vannak-e benne, vagy sem. Mindazonáltal a megjegyzések nagyon megkönnyítik a programkód megértését. Egysoros és több sorból álló megjegyzések ismerünk. Egy egysoros megjegyzés dupla per jelet (//) követően bármilyen karakter lehet. Egy egysoros megjegyzést befejez a soremelés. Egy több sorból álló megjegyzés /* karakterekkel kezdődik és a befejezése */ (lásd a 3. ábrát). A megjegyzéseket arra használjuk, hogy megmagyarázzuk a programkódot. Egy jó program mindig tartalmaz megjegyzéseket

12 3. ábra. Példa a programban lévő megjegyzésekre. A megjegyzéseket a kódolásban széles körben használják. Általában a kódokban szürke színnel jelölik őket. Mi szintén fogunk használni megjegyzéseket, hogy megmagyarázzuk a kódjainkat és érthetőbbé tegyük őket

13 Állandók és változók Az állandó és változó fogalmát egy fejezetben tárgyaljuk, mert ezek a fogalmak egymáshoz nagyon közeliek. Az állandó meghatározása Az állandó egy program része; egy objektum, amelynek van egy értéke. Egy programban levő állandó hasonló ahhoz, amit matematikai egyenletekben használnak. Ez egy változatlan érték. Egy algoritmikus nyelvben használt állandónak a leírása, sok tekintetben hasonlít a jól ismert fizikai és matematikai állandókhoz. Az emberiség felfedezte azokat az állandókat, amelyek semmilyen tekintetben nem függnek tőlünk. Ilyenek például a fizikában: a nehézségi gyorsulás, ami mindig egyenlő 9.8 m/s-mal; a matematikában: Pi = Ilyen fajta állandókat nem találunk egy algoritmikus nyelvben. Az állandó fogalmát matematikai egyenletekben használják. Például az Y = 3 * X + 7 egyenletben a 3 és 7 számok az állandók. Az ilyen állandók értékei teljesen attól a személytől függenek, aki létrehozta az egyenletet. Ezek az állandók a legközelibb analógiái azoknak, amiket az MQL4 programokban használunk. Egy állandót (értékként) a programozó a kód létrehozásakor meghatároz. Egy állandót csak az értéke jellemez, úgyhogy az állandó fogalma és az állandó értéke teljes szinonimák. Példa állandók: 37, 3.14, true, "Kazan" 4. ábra Egy állandó a PC memóriájában. Az állandók tulajdonságai Az állandó tulajdonsága az, hogy a program működési ideje alatt megtartja a programozó által adott értékét, és visszaadja ezt az értéket a programnak, amikor a program ezt kéri (5. ábra). A program mindegyik állandójának biztosítja a szükséges méretű részét a számítógép memóriájában. Egy állandó értékét a program végrehajtása alatt nem változtathatja meg sem programozó sem számítógép (6. ábra). Az állandó értéke mindig ugyanannyi marad. 5. ábra. Egy állandó a memóriacellában mikor visszaadja az érték a programnak

14 Egy állandó értékét nem változtathatjuk meg a program futása alatt. 6. ábra. Lehetetlen a program futása alatt megváltoztatni egy állandó értékét. A változó fogalma A változó egy programrész, aminek értéke és neve (azonosító) van. Az MQL4-ben levő változók meghatározása hasonló a matematikában elfogadotthoz. A különbség csak abban áll, hogy a matematikában levő változó értékére mindig utalnak, míg egy futó programban levő változó értékét elraktározzák egy különleges memóriacellában a számítógépben. A változó azonosító fogalma a változó nevének teljes szinonimája. A változót változó-névként beleteszi a kódszövegbe a szerző a kódolás során. A változónév (azonosító) betűkből, számjegyekből, és aláhúzás karakterből állhat, de betűvel kell kezdődnie. Az MQL4 nagybetű-érzékeny, vagyis, S és s nem ugyanaz. Példák változó nevekre: Alpha, alfa, beta, NuMbEr, Num, A_37, A37, qwerty_123 A következő változó azonosítók különböző változók neveit képviselik: a_22 és А_22; Massa és MASSA. Példák a változók értékeire: 37, 3.14, true, Kazan. A változó tulajdonságai Egy változó tulajdonsága az a képessége, hogy egy bizonyos értéket kapjon a programtól, a program futása alatt megtartsa ezt, és visszaadja ezt az értéket a programnak, mikor azt a program kéri. Mindegyik változónak a program a számítógépen kiutalja szükséges méretű részt a memóriában. Nézzük a 7. ábrát és figyeljük meg egy változó szerkezetét! 7. ábra Egy változó a számítógép memóriájában. A számítógép memóriacellájában a változónak van egy értéke. Ezt az értéket kérheti és megváltoztathatja működése során a program. Egy változó nevét soha nem változtathatják meg. Miközben a programozó egy kódot ír, a változónak bármilyen nevet be tud állítani. Azonban, amint a kész programot elindítjuk, sem programozónak, sem a számítógépnek, sem a programnak nincs semmilyen lehetősége arra, hogy megváltoztassa a változó nevét. Ha egy program a végrehajtása során találkozik egy változó nevével, a program hivatkozik erre a változóra, azért hogy megkapja az értékét. Ha a program hivatkozik egy változóra, ez utóbbi beállítja az értékét a programban. A változó értéke ugyanaz marad, a program csak a másolatát kapja annak az értéknek, ami a változót tartalmazó a memóriacellában található (8. ábra)

15 Amikor egy változó értékét beolvassuk egy programban, ez az érték változatlan marad. Egy változó nevét soha nem lehet megváltoztatni. 8. ábra. Egy változó memóriacellájának az állapota amikor beolvassuk annak érték a programba. Egy változó nem áll folyamatos kapcsolatban a futó programmal. Ez alatt az időszak alatt a program a szükséges számításokat végezheti vagy egy másik változóval foglalkozhat. A programmal való kapcsolat szünetében a változó megtartja az értékét, vagyis változatlan marad. A program algoritmusa szerint szükségessé válhat, hogy megváltoztassa egy változó értékét. Ebben az esetben a program beállítja a változó az új értékét, és a változó ezt az új értéket kapja a programtól. Minden szükséges módosítás megtörténik a memóriacellában. Vagyis a változó előző értékét törlik, míg a változó új értéke, amit a programtól kapott, átveszi a helyét (9. ábra). Egy változó értékét megváltoztathatja a program. A változó neve mindig változatlan. 9. ábra. Egy változó memóriacellájának az állapota mikor új értéket kap a programtól. Példa állandókra és változókra egy programban Egy programban állandókat és változókat találhatnak az operátorok. A lenti kódban A és B változók, 7 és 3 állandók: A = 7; // 1.sor B = A + 3; // 2.sor Tanuljunk meg, hogy egy program állandókkal és változókkal működik. Miközben végrehajtja ezeket a programsorokat, a program a következő lépéseket fogja csinálni: 1.sor: 2.sor: 1. A 7 állandó értékét beállítja a program. 2. Az A változó 7 értéket kap a programtól. 1. A program az egyenlőségjeltől jobbra talált egy kifejezést és megpróbálja kiszámítani azt. 2. A 3 állandó értékét beállítja a program

16 3. A program hivatkozik az A változóra. 4. A program beolvassa az A változó értékét (7). 5. A program számításokat végez (7 + 3). 6. A В változó a 10-es értéket kapja a programtól. Egy változó értékét a program futása alatt megváltoztathatjuk. Például ebben a programban lehet egy sor, ami a következőt tartalmazza: В = 33; // 3.sor Ebben az esetben a kész program végrehajtásánál a következő lesz: 1. A 33 állandó értékét beállítja a program. 2. A B változó a 33-as (új) értéket kapja a programból. Könnyű észrevenni, hogy a B változó a program futásának egy bizonyos pontján a 10-es értéket kapja, majd azután az értéke 33 lesz. A változó neve B mindezen események alatt változatlan marad, míg a változó értéke változni fog. 10. ábrán állandókat és változókat láthatunk a programkódban: 10. ábra. Állandók és változók egy programban

17 Adat típusok Közismert dolog, hogy csak egyféle dogokat adhatunk össze vagy vonhatunk ki egymásból. Például az almákat hozzá adhatjuk almákhoz, de az almákat nem adhatjuk hozzá négyzetméterekhez vagy a hőmérséklethez. Hasonló korlátozásokat találhatunk a modern algoritmikus nyelvekben. Miként a valóságos élet tárgyainak vannak bizonyos tulajdonságaik, amik jellemzik őket: a színük (piros, kék, sárga, zöld), az ízük (keserű, savanyú, édes), összegük (másfél, kettő, hét), az MQL4 is használja a különböző típusú adatokat. Miközben adattípusról beszélünk, gondolhatunk az állandó értékének egy fajtájára, egy változó értékre és egy függvény visszatérési értékére (a függvény fogalmát a Függvények részben tárgyaljuk). Az MQL4 a következő típusokat különbözteti meg (az állandók értékeit, változók értékeit és a függvény visszatérési értékét): int - egész számok; double - valós számok; bool - Boolean (logikai) értékek; string -string típusú érték; color - szín érték; datetime - dátum és idő. Int típus Az int típus értékei egész számok. Ez a típus olyan értékeket tartalmaz, amelyek a természetes egész számok. A következő értékek például egész számok: a chart-ablakban levő bárok összege (16000 bár), a nyitott és függőben levő megbízások száma (3 megbízás), távolság az aktuális ár, és a megbízás között - pontokban Open Price (15 pont). Az események számának összege szintén csak egész szám lehet. Például a megbízás nyitási kísérletek szám nem lehet másfél, csak egy lehet, kettő vagy három stb. Az integer típusnak két fajtája van: Decimális érték 0-tól 9-ig számjegyekből állhat, és lehet pozitív vagy negatív: 10, 11, 12, 1, 5,- +379, 25, ,-+1, 2. A hexadecimális érték latin betűkből állhat a-tól f-ig vagy A-tól F-ig és számjegyek 0-tól 9-ig. Ezeknek 0x-el vagy 0X-el kell kezdődniük és pozitív vagy negatív értékeket vehetnek föl: 0x1a7b, 0xff340, 0xAC3 0X2DF23, 0X13AAB, 0X1. Az int típus értékeinek tól a ig terjedő tartományon belül kell lenniük. Ha egy állandó vagy egy változó értéke a fenti tartományon túl van, a programművelet eredménye üres érték lesz. Az int típus állandók és változók értékei a számítógép memóriájában 4 byte helyet foglalnak le. Példa a programban levő int típusú változóra: int Art = 10; // Pálda az integer változóra int B_27 = -1; // Pálda az integer változóra int Num = 21; // Pálda az integer változóra int Max = ; // Pálda az integer változóra int Min = ; // Pálda az integer változóra Double típus A double típus értéke azok a valós számok, amik tört részt is tartalmaznak. Erre a típusra minta lehet bármilyen olyan érték aminek tört része van : a támaszvonal hajlásszöge, az instrumentum ára, a naponkénti megbízások átlaga. Néha problémát jelent, hogy amikor a változókat kell a kódba írni, nem mindig tiszta egy programozónak, hogy hová tartozik a változó (int vagy double). Lássunk egy kis példát:

18 Egy program egy héten belül adott 12 megbízást. Mi annak a változónak a típusa, ami megmutatja a naponkénti megbízások átlagos számát? A válasz nyilvánvaló: A = 12 megbízás / 5 nap. Ez azt jelenti, hogy a változót, aminek A = 2.4 az értéke, double-ként kell kezelni a programban, mert ennek az értéknek van tört része. Mi lesz a típusa ugyanannak a változónak, ha a megbízások teljes mennyisége egy hét alatt 10 lesz? Azt gondolnánk, ha a változó értéke 2 (10 megbízás / 5 nap = 2), nincs tört rész, a változó int típus lesz. Azonban ez az érvelés hibás. Egy változó törtrészének az értéke egyetlen nullából is állhat. Fontos, hogy ennek a változónak a típusa a természetéből fakadóan adódik. Ebben az esetben, a változónak szintén doubletípusúnak kell lennie. A tizedes pontot jelölni kell az állandót rögzítő rekordban: А = 2.0 A valós állandók és változók értékei egy egész számrészből, egy tizedespontból, és egy tört részből állnak. Az értékek pozitívak vagy negatívak lehetnek. Az egész számrész és a tört rész a 0-tól 9-ig terjedő számjegyekből állnak. A tizedes számjegyek száma a tizedespont után elérheti a 15-öt. Példa: 27.12; 1.0; ; ; ; A double típus értékei-+1.7 * e-308-től 1.7 * e308-ig terjedhetnek. A számítógép memóriában az állandók értékei és a double típus változói 8 byte helyet foglalnak el. Egy példa a programban használt double típusú változóra: double Art = ; // Pálda a valós változóra double B_27 = -1.0; // Pálda a valós változóra double Num = 0.5; // Pálda a valós változóra double MMM = ; // Pálda a valós változóra double Price_1 = ; // Pálda a valós változóra Bool típusú változó A bool típus értékei Boolean (logikai) értékek, valótlanságot vagy valóságot tartalmaznak. Azért hogy megismerjük a Boolean típus fogalmát, hozzunk egy egyszerű példát a mindennapi életünkből. Mondjuk, egy tanárnak elemeznie kell a tanulók tankönyveinek a meglétét. Ebben az esetben a tanár fel fog sorolni minden tanulót egy papírlapon, azután fel fogja jegyezni egy sorban azt, hogy egy tanulónak van-e egy bizonyos tankönyve, vagy nincs. Például a tanár pipa jeleket és kötőjeleket használhat a táblázatban: A tanulók listája Fizika tankönyv Biológia tankönyv Kémia tankönyv 1 Smith V Jones V - V 3 Brown - V V Thompson V V V Az oszlopokban csak 2 helyes érték lehet: igaz vagy hamis. Ezeket az értékek nem tartoznak a fenti típusok közül egyikbe sem, mert ezek nem számok. Ezek nem szín értékek, ízek, összegek, stb. azonban fontos jelentésük van. Az MQL4-ben az ilyen értékeket booleanoknak, vagy logikai, értékeknek elnevezzük. A bool típusú állandókat és változókat csak 2 lehetséges értékkel jellemezhetünk: igaz (True, TRUE, 1) vagy hamis (False, FALSE, 0). A bool típusú állandók és változók értékei számítógép memóriába 4 byte helyet foglalnak. Egy példa egy programban levő bool típusú változóra: bool aa = True; // Boolean változó аа értéke igaz bool B17 = TRUE; // Boolean változó B17 értéke igaz bool Hamma = 1; // Boolean változó Hamma értéke igaz bool Asd = False; // Boolean változó Asd értéke hamis

19 bool Nol = FALSE; // Boolean változó Nol értéke hamis bool Prim = 0; // Boolean változó Prim értéke hamis String típus Mindennapi életünkben hasonló, amikor például neveket, autótípusokat stb. tárolunk. A string típus értéke egy karaktersorozat, amit kettős idézőjelek közé zártak, (nem szabad összekeverni a kettős és egyszeres idézőjeleket!). Az idézőjeleket csak arra használjuk, hogy jelöljük egy string állandó kezdetét és végét. Maga az érték a karaktereknek az összessége, amiket az idézőjelek kereteznek. Ha egy dupla idézőjelet ( ) kell elhelyezni a stringben, akkor egy fordított perjelet (\) kell elé írni. Bármilyen speciális karaktert a fordított perjel (\) után elhelyezhetünk a stringben. Egy stringállandó hossza 0-tól 255 karakterig terjedhet. Ha egy stringállandó hossza felülmúlja a lehetséges maximumát, a többlet karaktereket a jobb oldalon meg fogja csonkítani a szerkesztő és figyelmeztetést fog adni. A fordított perjel (\) és egy karakter kombinációját, melyek közül az első a fordított perjel (\) általában elfogadja, és arra irányuló utasításként értelmezi a legtöbb program, hogy egy bizonyos szövegformázást végrehajtson. Ezt a kombinációt nem mutatják a szövegben. Például a \n kombinációja egy soremelést jelez; \t tabulálást kér, stb. A string típus értéke az a karaktersorozat, amit dupla idézőjelek kereteznek: "MetaTrader MetaTrader 4", " Stop Loss", "Ssssstop_Loss", "stoploss", "10 pips". A string értéke karakterekből áll. Az idézőjeleket csak arra használják, hogy megjelöljék a határait. A belső ábrázolása 8 byte méretű. Példa egy programban levő string típusú változókra: string Prefix = "MetaTrader 4"; // Példa a string változóra string Postfix = "_of_my_progr. OK"; // Példa a string változóra string Name_Mass = "History"; // Példa a string változóra string text ="Upper Line \n Lower Line"; // A szöveg soremelés utasítást tartalmaz Szín típus A színtípus értéke egy kromatikus érték. A szín jelentése (kék, piros, fehér, sárga, zöld, stb.) egy közismert dolog. Könnyű elképzelni, hogy egy változó vagy a szín típus egy állandója mit jelenthet. Ez egy állandó vagy egy változó, az az érték, ami egy szín. Ez úgy tűnhet, hogy kicsit szokatlan, de ez valójában nagyon egyszerű. Ahogy egy egész számállandó értéke egy szám, úgy egy színállandó értéke egy szín. A színállandók és változók értékeit három féle képpen tüntethetjük fel: Konstans A színtípusnak az értéke, amit konstansként tüntetünk fel, három részből áll, ami a három alapszín intenzitásának a számszerű értékét képviseli: piros, zöld és kék (RGB). Ennek a fajtának az értéke C -vel kezdődik, és egyszeres idézőjelek határolják. A terjedő RGB szín intenzitás számszerű értékei is fel lehet venni decimális és hexadecimális alakban. Példák: C'128,128,128' (szürke), C'0x00,0x00,0xFF' (kék), C'0xFF,0x33,0x00' (piros). Egész szám ábrázolás Az egész szám ábrázolást hexadecimális vagy decimális formában használjuk. Egy hexadecimális számot 0xRRGGBB formában alkalmazunk hol RR a piros intenzitás értéke, GG a zöldé és BB a kéké. A decimális ábrázolás közvetlenül nem tükrözi vissza az RGB szín értékeket. Csupán a hexadecimális számábrázolás tízes értékét képviselik

20 A színtípus értékeinek az ábrázolása egész számokként és hexadecimális konstansként nagyon felhasználóbarát. A modern szöveg és grafikai szerkesztők többsége a kiválasztott szín piros, zöld és kék összetevők intenzitásával kapcsolatos információt nyújt. Megteheted, hogy kiválasztasz egy színt a szerkesztődben és kimásolod az értéket, ami a megfelelő színt leírja, és ezt az értéket illeszted be a kódodban. Példák: 0 xffffff (fehér), 0x (zöld), (fehér), (zöld). 11. ábra. A konstans és az egész számmal ábrázolt színparaméterek a modern szerkesztőkből átvehetők

21 Színnevek A legkönnyebb módja annak, hogy beállítsunk egy színt, az, hogy a webes színek táblázata szerint megadjuk a nevét. Ebben az esetben egy szín értékét egy szóval jelezzük a szín angol nevével, például: Red - a piros szín. Black DarkGreen DarkSlateGray Olive Green Teal Navy Purple Maroon Indigo MidnightBlue DarkBlue DarkOliveGreen SaddleBrown ForestGreen OliveDrab SeaGreen DarkGoldenrod DarkSlateBlue Sienna MediumBlue Brown DarkTurquoise DimGray LightSeaGreen DarkViolet FireBrick MediumVioletRed MediumSeaGreen Chocolate Crimson SteelBlue Goldenrod MediumSpringGreen LawnGreen CadetBlue DarkOrchid YellowGreen LimeGreen OrangeRed DarkOrange Orange Gold Yellow Chartreuse Lime SpringGreen Aqua DeepSkyBlue Blue Magenta Red Gray SlateGray Peru BlueViolet LightSlateGray DeepPink MediumTurquoise DodgerBlue Turquoise RoyalBlue SlateBlue DarkKhaki IndianRed MediumOrchid GreenYellow MediumAquamarine DarkSeaGreen Tomato RosyBrown Orchid MediumPurple PaleVioletRed Coral CornflowerBlue DarkGray SandyBrown MediumSlateBlue Tan DarkSalmon BurlyWood HotPink Salmon Violet LightCoral SkyBlue LightSalmon Plum Khaki LightGreen Aquamarine Silver LightSkyBlue LightSteelBlue LightBlue PaleGreen Thistle PowderBlue PaleGoldenrod PaleTurquoise LightGray Wheat NavajoWhite Moccasin LightPink Gainsboro PeachPuff Pink Bisque LightGoldenrod BlanchedAlmond LemonChiffon Beige AntiqueWhite PapayaWhip Cornsilk LightYellow LightCyan Linen Lavender MistyRose OldLace WhiteSmoke Seashell Ivory Honeydew AliceBlue LavenderBlush MintCream Snow White A színtípusú állandók és a változók a számítógép memóriába 4 byte helyet foglalnak el. Példa a szín típus alkalmazására egy programban: color Paint_1 = C'128,128,128'; // A szín változó értéke szürke color Colo = C'0x00,0x00,0xFF'; // A szín változó értéke kék color BMP_4 = C'0xFF,0x33,0x00' // A szín változó értéke piros color K_12 = 0xFF3300; // A szín változó értéke piros color N_3 = 0x008000; // A szín változó értéke zöld color Color = ; // A szín változó értéke fehér color Alfa = 32768; // A szín változó értéke zöld color A = Red; // A szín változó értéke piros color B = Yellow; // A szín változó értéke sárga color Colorit = Black; // A szín változó értéke fekete color B_21 = White; // A szín változó értéke fehér Datetime típusú változó A datetime típus értéke a dátum és idő értéke. Ennek a típusnak az értékeit a programokban arra használhatjuk, hogy elemezzük az események kezdetét és az események végződését, például a fontos hírek kibocsátását, munkanapkezdést/befejezést, stb. A dátum és idő állandót úgy tüntethetjük fel, hogy az 6 részből tevődik össze: az év számszerű értéke, hónap, nap (vagy nap, hónap, év), óra, perc, és másodperc. Ez a típusú állandó D - vel kezdődik, és egyszeres idézőjelek keretezik. Megengedett megcsonkított értékeket használni: dátum nélkülit vagy idő nélkülit, vagy csak egy üres értéket. Értéke január 1- jétől december 31-éig terjed. A datetime típusú állandók és változók értékei a számítógép memóriába 4 byte-ot foglalnak el. Ez az érték az január 1-je 00:00-tól eltelt másodpercek száma. Egy példa a programban használható datetime típusú változókra: datetime Alfa = D' :00'; // New Year datetime Tim = D' '; // New Year

22 datetime Tims = D' :30:45'; // May 12, :30:45 p.m. datetime N_3 = D' :30:45'; // May 12, :30:45 p.m. datetime Compile = D''; // a jelenlegi ídő[a program lefordításának időpontja] Változó deklarációja és inicializálás Azért, hogy ne legyen kérdéses, hogy ez vagy az a változó milyen adattípushoz tartozik, szükségszerű hogy az MQL4 részletezze a változók fajtáit, a program legelején. Mielőtt egy változó részt vesz bármilyen számításban, azt deklarálni kell. A változó deklarációja a változó első említése a programban. Egy változó deklarációjánál a változó típusát meg kell adni. A változó inicializációja hozzárendel egy olyan kezdeti értéket a változóhoz, amely érték típusa megegyezik a változó deklarált típusával. Minden változót inicializálhatunk. Ha a kezdőértéket nem állítjuk be, egy számszerű változót inicializált értéke nulla (0) és egy string változó inicializált értéke egy üres sor. Az MQL4 megköveteli, hogy a változók típusát megadjuk a deklarációjuknál. Egy változó típusát deklarálják a változó nevének az első említésekor. A változó típusát a későbbiekben már nem kell megadni. A program végrehajtása során a változó értéke változhat, de a típusa és neve változatlanok maradnak. Egy változó típusát egyetlen sorban vagy operátorokkal is deklarálhatják. Egy változót deklarálhatunk egy sorban: int Var_1; // Változó deklaráció egy sorban Ez a rekord azt jelenti, hogy lesz egy Var_1 változó és ennek a változónak a típusa int lesz az adott programban. Egy sorban több ugyanolyan típusú változót is deklarálhatunk: int Var_1, Box, Comm; // Több változó deklarációja egy sorban Ez a rekord azt jelenti, hogy a programban lesz Var_1, Box és Comm, változó és mindegyik int típus. Ez azt jelenti, hogy a változókat, amiket fent felsoroltak, egész szám típusú változóként fogja kezelni a program. Változókat inicializálhatnak az operátorokkal: double Var_5 = 3.7; // Változó inicializációja értékadó operátorral Ez a rekord azt jelenti, hogy a programban lesz egy Var_5 nevű double típusú változó, aminek a kezdőértéke 3.7. A változó típusát később a programban már nem kell megadni. A későbbiekben minden alkalommal, amikor a program a változóval hajt végre műveletet, emlékszik a változó típusára, amit a deklarációkor adtunk a változónak. A program végrehajtása alatt a változók értékei változhatnak, de a típusuk változatlan marad. Egy változó nevének nincs kapcsolata a típusával, vagyis, nem tudod megítélni egy változó típusát a neve alapján. Egy nevet, amit egy változónak adtunk, más programokban használhatjuk bármilyen típusú változókra. Azonban bármilyen változó típusát egy programon belül csak egyszer lehet deklarálni. A változó deklarált típusát nem lehet megváltoztatni a program végrehajtása alatt. Példák változó deklarációjára és inicializálásra A változókat deklarálni lehet több sorban vagy egyetlen sorban

23 Megengedett több azonos típusú változót deklarálni egyidejűleg. Ebben az esetben változókat vesszőkkel választják el, és a sor végét egy pontosvessző zárja. 12. ábra. Példa a változók deklarációjára. A változó típusát csak egyszer deklaráljuk, amikor először említjük a változót a programban. A típust nem kell a későbbiekben megadni. 13. ábra. Példa a változók deklarációjára. Lehetséges, hogy operátorokkal deklaráljuk és inicializáljuk a változókat. 14. ábra. Példa változók inicializálására. 15. ábra. Változó inicializálás egy összetett operátor fejlécében

24 Műveletek és kifejezések Azért, hogy megértsük az MQL4-ben lévő műveleteknek és kifejezéseknek a fontosságát, különleges analógiákra nincs szükség. Gyakorlatilag ezek ugyanazok mint az egyszerű számtani műveletek és kifejezések. Mindenki megérti hogy az f = n + m rekordban,az f az n és az m a tagok vagy változók, az = és a + műveleti jelek, míg n + m egy kifejezés. A könyv előző részében bemutattuk a különböző adat típusokat. Itt meg fogjuk ismerni ezen adatok közti lehetséges kapcsolatokat (a négyzetmétert nem mindig adhatjuk hozzá az almákhoz). Természetes MQL4-ben is van néhány korlátozás és szabály, hogy hogyan használjuk a kifejezéseket és a műveleteket. Az Operand (operandus), az Operation (művelet) az Operation Symbol (műveleti jel) és Expression (kifejezés) meghatározása Az operandus egy állandó, egy változó, egy tömb elem vagy egy olyan érték, amit egy függvény adott vissza. (a függvényekről később fogunk tanulni a Függvények fejezetben, és a tömbökről a Tömbök bekezdésben; a tanulás jelenlegi szakaszában az operandusok megértéséhez elegendő amit az állandókról és változókról ezelőtt tanultunk). Más szóval az operandus egy művelet alaptagja. Az Operation (művelet) egy akció, amit az operandusokkal végeznek. Az Operation symbol (műveleti jel) egy előre meghatározott karakter vagy egy karakterekből álló csoport, az a parancs, hogy hajtsák végre a műveletet. Az Expression (kifejezés) az operandusok és műveleti jelek összetartozó csoportja; ez egy programrekord, ami egy adattípus kiszámított értékét jellemzi. A műveletek fajtái Az MQL4-ben a következő fajta műveletek vannak: számtani műveletek; értékadó műveletek; relációs műveletek; Boolean, kétértékű (logikai) műveletek; bitenkénti műveletek; vessző műveletek; függvényhívás. Műveleteket operátorokban használnak (lásd:operátorok).használatuknak csak az operátorokban van értelme egy programban. A műveleti jelek használatának lehetőségét meghatározzák az operátorok tulajdonságai (ha az operátorok tulajdonságai megengedik, hogy használj egy műveletet, akkor tudod használni azt; különben nem szabad használni ezt a műveletet). Tilos az operátorokon kívül használni a műveleti jeleket. Aritmetikai (számtani) műveletek A következő szimbólumok (műveleti jelek) tartoznak a számtani műveleti jelek közé: Szinbólum Művelet Példa Analógia + Értékek összeadása x Kivonás vagy előjelváltás x - 3, y = - y * Sorzás 3 * x / Osztás x / 5 % Az osztás maradéka A perc = az idő % A változó értékének növelése 1-el y++ y = y A változó értékének csökkentése 1-el y-- y = y - 1 Értékadó műveletek A következő szimbólumok az értékadó műveletekhez tartozó szimbólumok:

25 Szinbólum Művelet Példa Analógia = Az y változó x értékét kapja у = x += Az y változó növelése x-el у += x y = y + x -= Az y változó csökkentése x-el y -= x y = y - x *= Az y változó szorzása x-el y *= x y = y * x /= Az y változó osztása x-el y /= x y = y / x %= Az y változó x-el történő osztásának a maradéka y %= x y = y % x Relációs műveletek A következő szimbólumok a relációs műveletekhez tartozó szimbólumok: Szinbólum Művelet Példa == Igaz, ha x egyenlő y x == y!= Igaz, ha x nem egyenlő y x!= y < Igaz, ha x kevesebb mint y x < y > Igaz, ha x kevesebb mint y x > y <= Igaz, ha x egyenlő vagy kevesebb mint y x <= y >= Igaz, ha x egyenlő vagy nagyobb mint y x >= y Boolean (kétértékű) (logikai) műveletek A következő szimbólumok a Boolean műveletek szimbólumai: Szimbólum Művelet Példa Magyarázat! NOT (NEM) (logikai tagadás)! х Igaz(1), ha az operandus értéke Hamis(0); Hamis(0) ha az operandus értéke nem Hamis(0) OR (VAGY) (logikai választás) x < 5 x > 7 Igaz(1), ha bármelyik feltétel igaz && AND (ÉS) (logikai összeadás) x == 3 && y < 5 Igaz(1), ha minden feltétel igaz Bitenkénti műveletek Bitenkénti műveleteket csak egész számokkal hajthatunk végre. A következő műveletek a bitenkénti műveletekhez tartoznak: A kettes számrendszerben ábrázolt számok értékének a komplemense. A kifejezés értéke 1 minden olyan helyen, ahol a változó értékei 0, és 0 minden olyan helyen, ahol a változó értéke 1. b = ~n; A kettes számrendszerben ábrázolt számok számjegyeinek elmozdítása jobbra. Ez a jobbra léptetés logikusan azt is jelenti, hogy minden helyet, ami kiürült a bal oldalon nullák fognak kitölteni. x = x >> y; A kettes számrendszerben ábrázolt számok számjegyeinek elmozdítása balra. Minden helyet, ami kiürült a jobb oldalon nullák fognak kitölteni. x = x << y; A kettes számrendszerben ábrázolt x és y változókon végrehajtott bitenkénti AND ( ÉS) művelet. A kifejezés értéke 1 (Igaz) minden helyen, ahol sem x sem y nem nulla; és a kifejezés értéke 0 (Hamis) minden másik helyen

26 b = ((x & y)!= 0); A kettes számrendszerben ábrázolt x és y változókon végrehajtott bitenkénti OR (VAGY) művelet. A kifejezés értéke 1 (Igaz) minden helyen, ahol x vagy y nem nulla; és a kifejezés értéke 0 (Hamis) minden másik helyen. b = x y; A kettes számrendszerben ábrázolt x és y változókon végrehajtott bitenkénti EXCLUSIVE OR (KIZÁRÓ VAGY) művelet. A kifejezés értéke 1 (Igaz) minden helyen, ahol x és y különböző bináris értékű; és a kifejezés értéke 0 (Hamis) minden másik helyen. b = x ^ y; Comma (vessző művelet) A vesszőkkel elválasztott kifejezéseket balról jobbra számolják ki. A bal oldali kifejezésben levő számítások eredménye hatással van a jobbra lévő kifejezésre. Az eredmény típusát és értékét a jobb oldalon lévő kifejezés fajtája és értéke adja. for (i=0, j=99; i<100; i++, j--) Print(array[i][j]); // Ciklus (hurok) utasítás Az átvitt paraméterlistát (lásd később) hoztuk fel példaként. My_function (Alf, Bet, Gam, Del) // Függvényhívás a paraméterek megadásával Az operátorokról és függvényekről részletesebben az Operátorok és a Függvények szakaszokban és az Operátorok fejezetben olvashatunk. Függvényhívás A függvényhívás részletes leírása a Függvényhívás bekezdésben. Hasonló operandusokon végrehajtott műveletek Ha egy általános iskolai tanulónak azt mondanák, hogy egy olyan példa megoldása közben amiben ceruzák száma szerepel, neki operandusokat, műveleteket és kifejezéseket kell használnia, a szegény iskolás gyerek biztosan lehetetlennek találná azt. Miközben megnézi az operációk szimbólumait, azt gondolhatja, hogy a kódolás egy rejtélyes és nagyon bonyolult eljárás, csak egy fajta elit számára hozzáférhető. Mindazonáltal a kódolás egyáltalán nem nehéz. Ennek bizonyítására álljon itt néhány példa. 1. feladat: Johnnak van 2 ceruzája, Pete -nek van 3 ceruzája. Ezeknek a fiúknak hány ceruzája van összesen?:) Megoldás: Legyen John ceruzáinak a száma A változó és Pete ceruzáinak száma B változó, míg az eredményt C- vel fogjuk jelölni. A válasz ez lesz: С = А + В Az Adat típusok szakaszban bemutattuk a változó deklarációjának módszereit. A ceruzák dolgok, azaz, ez valami, ami alapvetően részként létezhet, (például lehet fél ceruza). Így ceruzákat reálisként változóként fogjuk deklarálni, azaz a változó típusa double. Tehát a megoldást például következőképpen tudjuk, kódolni: double A = 2.0; // John ceruzáinak száma double B = 3.0; // Pete ceruzáinak száma double C = A + B; // Az összeg Ebben az esetben, nyilvánvaló, hogy a "+" műveletet azonos típusú változókkal végeztük. A alábbi kifejezés értékének a típusa: A + B A változóknak az a típusa lesz, mint a kifejezés összetevőinek a típusa. Jelen esetben ez double típus lesz

27 Hasonló választ fogunk kapni az értékek közti különbségre (arra, hogy mennyivel több ceruzája van Pete-nek Johnnál?): double A = 2.0; // John ceruzáinak száma double B = 3.0; // Pete ceruzáinak száma double C = B - A; // Két valós szám közötti különbség A többi számtani műveletet hasonló módon használjuk: double C = B * A; // Két valós szám szorzata double C = B / A; // Két valós szám hányadosa Szintén hasonló számításokat hajthatunk végre az egész számokkal is. ( in típus) 2. feladat: A tanulók az órán a táblához mentek felelni. John 2-szer ment, Pete 3-szor. A fiúk hányszor mentek a táblához összesen? Megoldás. Legyen John útjainak száma X változó, Pete útjainak száma Y változó, és az eredmény legyen Z. Ebben a példában, nekünk int típusú változót kell használnunk az események természetéből fakadóan (nem tudsz a táblához menni 0.5-ször vagy 1.5-ször; a táblánál a felelet lehet jó vagy rossz, de minket csak a feleletek száma érdekel). Ennek a problémának a megoldását így írhatjuk, hogy: int X = 2; // John útjainak száma int Y = 3; // Pete útjainak száma int Z = X + Y; // Az összeg A különbség számításának az esetében vagy a szorzási és osztási műveleteknél is hasonló egyszerű módon járunk el: int X = 2; // Egész int Y = 3; // Egész int Z = Y - X; // Két egész szám különbsége int Z = Y * X; // Két egész szám szorzata int Z = Y / X; // Két egész szám hányadosa A helyzet a string típusú változóval egy kicsit más: 3.feladat: Egy a ház sarkánál van egy fűszerraktár, a neve "Arctic". Ugyanannak a háznak a másik sarkánál egy női fodrászat a neve "Hairdressing Saloon". Mit írnak a házra? Megoldás. az MQL4-ben megengedett, hogy összeadjuk a string típusú állandókat és változókat. Ha hozzá adunk egy string típusú változót, egy másik string típusú változóhoz, akkor egész egyszerűen egy sorban jelennek meg a kifejezésben. Könnyű kódolni egy olyan programot, ami megadja a szükséges választ: string W1 = "Arctic"; // 1.string string W2 = "Hairdressing Saloon"; // 2.String string Ans = W1 + W2; // A stringek összege Az Ans változó típusa string lesz ami következőképpen jelenik meg: ArcticHairdressing Saloon Nem történt semmi váratlan, helyesen alakult a string értéke. A gyakorlati kódolás során természetesen oda kell figyelni a szóközökre és egyéb központozásra. Bármilyen más számtani művelet a string típusú változókkal tilos:

28 string Ans= W1 - W2; // Tilos string Ans= W1 * W2; // Tilos string Ans= W1 / W2; // Tilos Typecasting (A változó típusának módosítása) A typecasting (típusmódosítás) egy operandus vagy egy kifejezés típusának módosítása (megfeleltetése). A műveletek végrehajtása előtt módosítjuk az operandusok típusát (az értékadó műveletet kivéve), a legmagasabb prioritású típusú operandus típusára, míg az értékadó műveletek esetén, a végrehajtás előtt a céltípushoz módosítjuk őket. Nézzük hogyan lehet kezelni a problémákat a typecastinggel! 4. feladat: Johnnak 2 ceruzája van, míg Pete 3-szor ment a táblához. Mennyi ez összesen? Ami a formális gondolkodást illeti, a probléma nyilvánvalóan megoldhatatlan. Magától értetődik, hogy az eseményeket nem lehet összeadni a dolgokkal. 5. feladat: Egy a ház sarkánál van egy fűszerraktár, a neve "Arctic", míg Johnnak van 2 ceruzája.:) Szintén reménytelen (a hétköznapi gondolkodás szerint) bármelyik kérdést feltenni: 1. Összesen mennyi? vagy 2. Mi van írva a házra? Ha helyesen akarod megoldani mindkét fenti problémát az MQL4 szabályai szerint, ismerd meg a typecasting szabályokat. Először is arról kell beszélgetnünk, hogy a különböző típusú változók hogyan képviseltetik magukat a számítógép memóriájában. Azok az adattípusok, mint például az int, bool, color, datetime és double, a számszerű adattípusokhoz tartoznak. Az int, double, bool, color és a datetime állandók belső ábrázolása egy szám. Az int, bool, color és a datetime változókat egész számok képviselik a számítógép memóriájában, míg a double típusú változók lebegőpontos azaz valós számok. Az string tipusú állandók és változók karakterek. (16. ábra). Az int, bool, color és a datetime értékeit egész számok képviselik a számítógép-memóriában. A double típus értékeit valós számok képviselik a számítógép-memóriában. A string értékeit karakterek képviseli a számítógép-memóriában. Az int, bool, color, datetime és a double típusok értékei a számszerű értékek. Astring értékei a karaktertípusú értékek

29 16. ábra. A számítógép-memóriában levő különböző adattípusok ábrázolása. Megemlítettük fent, hogy az int, bool, color és a datetime változóknak az értékeit egész számok képviselik a számítógép-memóriában, míg a double típusúakat - valós számok. Tehát ha arra vagyunk kíváncsiak, hogy egy olyan kifejezésnek a típusa ami különböző típusú változókból áll milyen lesz, akkor csak három adattípus között tudunk választani: int, double és string. Abool, color és datetime értékei egy kifejezésben ugyanúgy viselkednek mint az in típus értékei. Tehát milyen típus lesz egy kifejezésnek az értéke, ami különböző típusú operandusokból állt? MQL4- ben a következő typecasting szabályok érvényesülnek: ha a kifejezés különböző típusok operandusait tartalmazza, a kifejezés típusa a legmagasabb prioritású típus lesz; az int, bool, color a datetimeegyenlő prioritásúak, míg a double típusnak magasabb prioritása van, és a string típus prioritása a legmagasabb; ha az értékadó műveleti jelnek a jobb oldalán levő kifejezés típusa nem esik egybe a műveleti jel bal oldalán levő változó típusával, ennek a kifejezésnek a típusát az értékadó műveleti jel bal oldalán levő változó típusa adja; ezt hívják a cél-típus módosításnak; tilos a string tipust bármilyen más típusra változtatni. És most térjünk vissza a 4.feladathoz! Két megoldás is kínálkozik. 4.1 megoldás: in típusú eredmény számítása: double A = 2.0; // John ceruzáinak a száma int Y = 3; // Pete útjainak a száma int F = A + Y; // Az eredmény Először is, nekünk tudnunk kell a kifejezés értékét, feltéve, hogy az operandusai különböző típusúak. A következő kifejezésben: A + Y, két operandus adattípust használnak: А - double típus, Y in típus. A typecastinggel kapcsolatban megismert szabállyal összhangban, ennek a kifejezésnek az értéke double típusú szám lesz. Jegyezzük meg: Csak az A+Y kifejezés típusáról beszélünk, de nem az F változó tipusáról ami az értékadó műveleti jel bal oldalán szerepel. Ennek a kifejezésnek az értéke 5.0 valós szám. Azért hogy megállapítsuk az A+Y kifejezés tipusára alkalmazzuk az előző részben megismert typecasting szabály

30 Az A+Y kifejezés kiszámítása után az értékadó műveletet végrehajtják. Ebben az esetben, a tipus hibásan illeszkedik: az A+Y kifejezés típusa double, míg az F változó int. Az értékadó műveletet végrehajtása alatt: Először az A+Y kifejezést int-ként alkalmazzák (az egész számok számítási szabálya szerint) és egész számmá válik: 5; akkor ez az eredmény válik az F változó értékévé. A számításokat végrehajtásának a második része a cél-típus módosítás szabály alkalmazása. A számítások és manipulációk végeredménye következő : Az F változó típusa int értéke 5 egész szám megoldás: hasonló a helyzet, ha arra törekszünk, hogy a double típusú érték legyen egy eredményünk: double A = 2.0; // John ceruzáinak száma int Y = 3; // Pete útjainak a száma double F = A + Y; // Az eredmény Ez a helyzet abban különbözik az előzőtől, hogy a F célváltozó típusa (az értékadó operátor bal oldalán) double ami egybeesik az A+Y kifejezés típusával (double) tehát nekünk itt nincs semmilyen céltípusmódosító feladatunk. A számítások eredménye (az F változó értéke double) valós szám: 5.0. Most keressünk egy megoldást, az 5. feladatban felmerült kérdésre a változók inicializálásánál: string W1 = "Arctic"; // 1. string double A = 2; // John ceruzáinak a száma 5.1 megoldás: A lehetséges megoldás erre a problémára: string W1 = "Arctic"; // i. string double A = 2; // John ceruzáinak a száma string Sum = W1 + A; // A jobb oldalon létrejövő transzformáció Itt a jobb oldalon összeadjuk két változó értékei: egy string típusút, és egy double típusút. A typecasting szabály szerint az A változó értékét először string típusra módosítjuk (mivel ez a típus magasabb prioritású), azután az azonos típusú értékeket összeadjuk (összefűzés). Az értékadó operátor jobb oldalán levő eredmény típusa string lesz. A következő lépésben ezt az értéket kapja Sum string változó. Végül Sum változó értéke a következő string lesz: Arctic megoldás: Ez a megoldás hibás: string W1 = "Arctic"; // 1. string double A = 2; // John ceruzáinak a száma double Sum = W1 + A; // Ez nem elfogadható Ebben az esetben, megszegjük a cél-típus módosítás egyik tilalmát, mivel a string típust módosítjuk. A W1+A, kifejezés típusa az előző megoldásban string volt. Amikor az értékadó műveletet végrehajtjuk, a cél-típus módosítást is végre kell hajtani. Azonban a fentebbi szabály szerint a string típus módosítása alacsonyabb prioritású típusra tilos. Ez egy olyan hiba, amit észlelni fog MetaEditor a program létrehozásánál (a fordításánál). Általában, a betartandó szabályok ezen része tiszta és egyszerű: Ha kiszámítasz bármilyen értéket, neked a legmagasabb prioritással rendelkező típust kell céltípusnak választanod. Typecasting alacsonyabb prioritásra csak számszerű értékekel megengedett, míg a stringek nem változhatnak át számokká. Az egész számokkal végzett számítások Az egész számok a tört rész nélkül számok. Ha összeadjuk vagy kivonjuk őket, szemléletes eredményt fogunk kapni. Például, ha: int X = 2; // Első int változó int Y = 3; // Második int változó és:

31 int Z = X + Y; // Összeadás nem probléma kiszámítani a Z változó értékét: = 5 Hasonlóan, ha egy szorzási műveletet hajtunk végre: int Z = X * Y; // Szorzási művelet, az eredmény megjósolható: 2 * 3 = 6 De milyen eredményt kapunk, ha a programunknak végre kell hajtania egy osztást? int Z = X / Y; // Osztási művelet Könnyű 2 / 3-ot írni. Azonban ez nem egy egész szám. Tehát mi lesz a X/Y kifejezés értéke és a Z változó? Az egész szám (int) számítási szabálya abban áll, hogy a tört részt eldobjuk. A fenti példában az egyenlőségjelnek a jobb oldalán levő kifejezés csak egész számokat tartalmaz, tehát ebben az esetben typecasting nem történik. És ez azt jelenti, hogy az X/Y kifejezés típusa int és a X/Y (=2/3) kifejezés egész értéke 0 (nulla). Ezt az értéket (nulla) fogja kapni a Z változó. Természetesen, az X és Y változók más értékeire másfajta eredményt fogunk kapni. Például, ha: int X = 7; // Egy int változó értéke int Y = 3; // Egy int változó értéke int Z = X / Y; // Osztási művelet, akkor 7/ 3 lesz az X/ Y kifejezés értéke és a Z változó 2 (kettő) lesz. A műveletek rendje A számítási szabály a következőben áll: Egy kifejezés értékét a prioritások szerint aritmetikai műveletekkel és balról jobbra kell kiszámolni, a typecasting szabályt mindegyik közbenső eredménynél be kell tartani. Szemléltessük a számítás menetét a következő példával: Y = 2.0*( 3*X/Z - N) + D; // Példa kifejezés Az egyenlőségjel jobb oldalán levő kifejezés két összetevőből áll: 2.0*( 3*X/Z - n) és D. A 2.0*( 3*X/Z - n) pedig két tényezőből: 2.0 és (3*X/Z - n). A zárójelben lévő 3*X/Z - n két összetevőből áll, és végül a 3*X/Z összetevő három tényezőből áll mégpedig: 3, X és Z. Azért hogy az egyenlőségjeltől jobbra lévő kifejezést kiszámítsuk, először ki fogjuk számolni a 3*X/Z kifejezést. Ez a kifejezés két műveletet tartalmaz (szorzás és osztás) ezek azonos rangúak, tehát ezt a kifejezést balról jobbra haladva számoljuk ki. Először ki fogjuk számítani a 3*X kifejezés értékét, az eredmény típusa ugyanaz lesz, mint az X változónak. Ezzel a típussal ki fogjuk számítani, a 3*X/Z kifejezés értékét és típusát, ami a typecasting szabály szerint kapunk. Azután a program ki fogja számítani a 3*X/Z- n kifejezés értéket és fajtáját, azután - a 2.0*(3*X/Z- n) kifejezést, és végül az egész 2.0*(3*X/Z- n) + D kifejezést. Könnyű belátni, hogy egy programban levő műveletek rendje a matematikaihoz hasonló. Mindazonáltal a korábbi különböző közbenső számítások eredményének a típusa és értéke jelentős befolyást gyakorol a számítások végeredményére. Különösen (a matematikában elfogadott

32 szabályoktól eltérően), egy kifejezésben levő műveletek sorrendje nagyon fontos. Hogy ezt demonstráljuk, fontoljunk meg egy kis példát. 6. feladat: számoljuk ki az А/В*С és А*С/В kifejezések értékeit, ha А, В, és С egész számok. A számítás eredményével kapcsolatban intuitív módon arra számítanánk, hogy mindkét esetben ugyanaz. Azonban ez a feltételezés csak valós számokra igaz. Ha in típusú operandusokból álló kifejezések értékeit akarjuk kiszámítani, mindig figyelembe kell vennünk a közbenső eredményt. Ilyen esetben az operandusok sorrendje alapvető fontosságú: int A = 3; // Int tipusú érték int B = 5; // Int tipusú érték int C = 6; // Int tipusú érték int Res_1 = A/B*C; // Eredmény 0 (nulla) int Res_2 = A*C/B; // Eredmény 3 (három) Előbb számítsuk ki az A/B*C: kifejezést 1. Először (balról jobbra) az A/B kifejezés értékét kiszámoljuk ki. A fenti szabályok szerint, a kifejezés (3/5) egész szám értéke 0 (nulla). 2. Kiszámolva a 0*С kifejezés (nulla szorozva С). Az eredmény 0 (nulla) egész szám. 3. Végeredmény (a Res_1 változó értéke) 0 (nulla) egész szám. Most kövessük azt az utat, hogy kiszámítjuk az A*C/B kifejezést! 1. A*C kiszámítása. Ennek a kifejezésnek az értéke egész szám: 18 (3*6=18). 2. A 18/B kifejezés kiszámítása. A válasz nyilvánvaló: a tört részt eldobtuk, (18/5) eredménye 3 (három) egész szám. 3. Végeredmény (a Res_2 változó értéke) 3 (három) egész szám. A fenti példa csak egy kis kódrészlet, amiben az in típusú változók értékeivel számoltunk. Ha lecseréljük ezeket a változókat ugyanolyan értékű állandókra, a végeredmény ugyanaz lesz. Miközben egész számokat tartalmazó kifejezéseket számítasz ki, figyelned kell a programsoraid tartalmára. Különben hiba történhet a kódodban, amit később felfedezni és javítani nagyon nehéz, (különösen nagy programokban). Ilyen hibák nem történnek valós számokkal végzett számításokban. Ha egy összetett kifejezésben a különböző típusok operandusait használod, a végeredmény egy olyan véletlenszerűen kialakított résztől függhet, ami egész számokat tartalmaz. Az Operátorok bekezdésben az operátorok meghatározását és általános tulajdonságait tárgyaljuk, míg az egyes operátorok különleges tulajdonságait az Operátorok fejezetben írjuk le

33 Operátorok Az operátor fogalma Minden programnyelv fő témái közül az egyik az operátor fogalma. A kódolás lehetetlen feladat azon személy számára, aki nem tanulta meg teljesen ezt az fogalmat. Azonban miután egy programozó alaposan elsajátította az operátorokkal kapcsolatos ismereteket, már nincs messze attól, hogy saját programokat írjon. Az operátor a program része; egy olyan frázis algoritmikus nyelven, ami előírja az adat átalakítás módszerét. Minden program tartalmaz operátorokat. Az operátor legközelibb analógiája egy mondat. Ahogy a közönséges mondatok alkotják egy történet vagy egy regény szövegét, úgy az operátorok alkotják a programot. Az operátorok tulajdonságai Az operátoroknak kétfajta tulajdonsága van: a közös tulajdonság és az egyes operátorok speciális tulajdonságai. Az operátorok közös tulajdonsága Minden operátornak egy közös tulajdonsága van az, hogy őket mind végrehajtják. Azt mondhatjuk, hogy az operátor egy utasítás, ami az útmutatót tartalmazza egy művelet végrehajtásához (egy parancs leírása). Egy számítógép, miközben futtatja a programot (folyamatosan feldolgozza az operátorokat, egyiket a másik után) végrehajtja a parancsokat (receptek, utasítások) amiket az operátorok előírnak. Operátor, mint ilyen csak egy rekord, egy karaktercsoport. Az operátorra semmi nincs befolyással a program alatt. Ezért, amikor egy számítógép végrehajt egy programot, semmi nem történik az operátorokkal, továbbra is maradnak a programban, ahogy a programozó azt megírta. Ha a PC-d végrehajtotta az adatokkal azokat a műveleteket, amelyeket az operátor tartalmaz, azt mondjuk: az operátort végrehajtották. Az operátorok speciális tulajdonságai Több fajta operátor van. Mindegyik operátornak vannak speciális tulajdonságai. Például egy értékadó operátornak megvan az a képessége, hogy egy bizonyos értéket jelöljön ki az adott változónak; egy ciklus operátor tulajdonsága a többszörös végrehajtás, stb. Az operátorok speciális tulajdonságait részletesen tárgyaljuk a könyv Operátorok című fejezetében. Itt csak arra hívjuk fel figyelmet, hogy minden operátor típusnak vannak sajátos tulajdonságai, amik csak az adott típusra jellemzőek és nem találhatók meg semmilyen másik operátorban. Az operátorok típusai Két fajta operátor van: egyszerű operátorok és összetettek

34 Egyszerű operátorok Az egyszerű operátorok az MQL4ben a ";" (pontosvessző) karakterrel érnek véget. Ezen elválasztót használva, a PC érzékeli, hogy egy operátor hol ér véget és egy másik hol kezdődik. Ez a karakter (a pontosvessző) olyan szerepet tölt be a egy programban, mint a. (pont) a különálló mondatok között egy normális szövegben. Egy operátor több sorban is elhelyezkedhet. Több operátor is lehet egy sorban. Minden egyszerű operátor "; (pontosvessző) karakterrel ér véget. Példák egyszerű operátorokra: Day_Next= TimeDayOfWeek(Mas_Big[n][0]+60); // Egyszerű operátor Go_My_Function_ind(); // Egyszerű operátor a=3; b=a*x+n; i++; // Tőbb operátor egy sorban Print(" Day= ",TimeDay(Mas_Big[s][0]), // Egy operátor.. " Hour=",TimeHour(Mas_Big[s][0]), // több.. " Minute=",TimeMinute(Mas_Big[s][0]),// sorban.. " Mas_Big[s][0]= ",Mas_Big[s][0], // helyezkedik... " Mas_Big[s][1]= ",Mas_Big[s][1]); // el Összetett operátorok (compoundok) Egy összetett operátor több ";" által elválasztott operátorból áll és kapcsos zárójelek közé van zárva. Az egyszerűség kedvéért, több operátornak egy operátorként történő használata esetén, a programozók használják az összetett operátorokat (hívják még blokknak vagy blokk kódnak). Az összetett operátorok listáját kapcsos zárójelek határolják. Egy záró kapcsos zárójel jelöli egy összetett operátor végét Egy példa a feltételes operátorban levő compound használatára. Ez az if operátor egy feltétellel kezdődik, amit egy compound operátor követ. Az összetett operátor tartalmazza a végrehajtandó operátorok listáját. 17. ábra. Összetett (compound) operátor. Egy összetett operátor testét kapcsos zárójelek határolják. Minden összetett operátor egy záró kapcsos zárójellel ér véget

35 Példák összetett operátorokra: // Példa a switch operátorra switch(ii) // Switch operátor // Nyitó kapcsos zárójel case 1: Buf_1[Pok-f+i]= Prognoz; break; // Beágyazott operátor (operátor test) case 2: Buf_2[Pok-f+i]= Prognoz; break; // Beágyazott operátor (operátor test) case 3: Buf_3[Pok-f+i]= Prognoz; break; // Beágyazott operátor (operátor test) // Záró kapcsos zárójel... //.. jelzi a compaund operátor végét // // Példa a loop (hurok) használatára for (tt=1; tt<=kol_point[7]; tt++) // A for operátor // Nyitó kapcsos zárójel Numb = Numb + Y_raz[tt]*X_raz[ii][tt]; // Beágyazott operátor (operátor test) // Záró kapcsos zárójel... //.. jelzi a compaund operátor végét // // Példa az if feltételes operátorra if (TimeDay(Mas_Big[f][0])!= 6) // if ) // Nyitó kapcsos zárójel Sred =(Nabor_Koef[ii][vv][2]+ NBh)*Point;// Beágyazott operátor (operátor test) Ind = Nabor_Koef[ii][vv][0] + f; // Beágyazott operátor (operátor test) Print(" Ind= ",Ind); // Beágyazott operátor (operátor test) // Záró kapcsos zárójel... //.. jelzi a compaund operátor végét Egy compound testét mindig kapcsos zárójelek határolják és nulla, egy vagy több operátorból állhat. Példák egyszerű operátorokra: // // Példa a for operátorra for (n=1; n<=numb; n++) // for Mas[n]= Const_1+ n*pi; // Beágyazott operátor (operátor test) // // Példa az if feltételes operátorra if (Table > Chair) // if Norma = true; // első operátor (suboperator 1) else // Máskülönben Norma = false; // második operátor (suboperator 2) // Több egyszerű operátort lehet kombinálni egy összetett operátorban minden további feltétel nélkül. Ez egy ritka, de teljesen elfogadható szerkezet. Ebben az esetben az operátorokat, amelyeket kapcsos zárójelek közé zártak operátor blokknak nevezzük. Ez a gyakorlat teljesen elfogadható. A programozó döntheti el, hogy kapcsos zárójelek közé csoportosítsa-e az operátorokat, a kényelmes kódábrázolás kedvéért ez egy jó megoldás. Egy példa az operátorok blokkjára: // Nyitó kapcsos zárójel Day_Next= TimeDayOfWeek(Mas_Big[n][0]+60); // Egyszerű operátor b=a*x+n; // Egyszerű operátor // Záró kapcsos zárójel

36 Az operátorokkal kapcsolatos követelmények Az operátorokat egy program szövegében a formátum szabályok szerint kell írni (ahogy őket a kódban ábrázolni kell). Az operátor nem szegheti meg ezeket a szabályokat. Ha a program olyan operátort tartalmaz, amit a formátumszabályok megsértésével írtak, a MetaEditor egy hibaüzenetet fog adni. Ez azt jelenti, hogy a programot, ami hibás operátort tartalmaz nem használhatjuk. Neked ismerned kell az adott fajta operátorok formátumának "operator format" tipikus szabályait. Mindegyik fajta operátornak saját formátuma van. Az operátor formátumok részletes szabályai ennek a könyvnek az Operátorok című fejezetében olvashatók. Az operátorok végrehajtási rendje Minden program egy nagyon fontos jellemzője az operátorok végrehajtásának a sorrendje. Az operátorokat nem lehet végrehajtani ok nélkül vagy szabálytalan sorrendben. Az MQL4 az operátorok végrehajtását következő rendben fogadja el: Az operátorokat olyan sorrendben hajtják végre, ahogy elhelyezkednek a programban. Az operátorok végrehajtási iránya fentről lefelé és balról jobbra történik. Ez azt jelenti, hogy az egyszerű és az összetett operátorokat is egyesével hajtják végre (mint a versekben lévő sorokat: először elolvassuk a legfelső sort, azután az alatta lévőt, azután a következő és így tovább). Ha egy sorban több operátor van, őket egymás után kell végre hajtani, balról jobbra, azután pedig át kell térni a következő sorra. A compound operátorokat, ugyanígy hajtjuk végre, a blokkban lévő operátorokat csak akkor lehet végrehajtani, ha már az előtte lévő operátorokat végrehajtottuk. Az operátorok írása és végrehajtása: példák Az a programszöveg, amely operátorokat tartalmaz, nem sokban tér el egy normális szövegtől vagy egy matematikai jelrendszertől. Mindazonáltal ez a hasonlóság csak formális. Egy normális szöveg megengedi, hogy a lejegyzését tetszőleges sorrendbe tegyék, míg neked a programban tartanod kell egy egyértelmű rendet. Példaként lássuk, hogy egy értékadó operátor hogyan működik. Meg fogunk oldani egy egyszerű elsőfokú egyenletrendszert és össze fogjuk hasonlítani néhány matematikai számítás ábrázolását egy normális szövegben és egy programkódban. 7. feladat: Nekünk van egy egyenletrendszerünk: Y = 5 Y - X = 2 Az X változó számszerű értékét kell meg találni. 1. megoldás: A normális szöveg egy papírlapon: Х = 2 2. Х = Х = 3 2. megoldás: a program szövege:

37 Y = 5; // 1. sor X = Y - 2; // 2. sor Az első és a második megoldásban is a feljegyzéseknek (sorok) van egy befejezett tartalma. Mindazonáltal az 1. megoldás sorait nem használhatjuk egy programban ebben a formában, mert a megjelenésük nem felel meg az értékadó operátor formátumának. Az 1. megoldás a papíralapú feljegyzés néhány függőséget képvisel. Ezt csak arra tudjuk használni, hogy a változók közti kapcsolatokról tájékoztassák a programozót. Az operátoroknak egy programban más a célja - tájékoztatni a gépet milyen műveletek vannak és ezeket milyen sorrendben kell végre hajtania. Kivételek nélkül minden operátornak olyan pontos utasításokat kell adni, amelyek nem engednek meg kétértelműséget. A 2. példában mindkét operátor értékadó operátor. Minden értékadó operátor a gépnek szó szerint a következő utasítást adja: Számítsd ki a kifejezés értékét az egyenlőségjel jobb oldalán és rendeld hozzá ezt az értéket az egyenlőségjel bal oldalán levő változóhoz. Ez okból, egy értékadó operátorban az egyenlőségjel bal oldalán csakis a változó és semmi más nem lehet. Például, az 5- Х = 2 rekord használata hibát tartalmaz, mert az 5 - Х karakter csoport nem egy változó. Ezért nincs memóriacella kijelölve, ahová az egyenlőségjel jobb oldalán szereplő kifejezés számszerű értékét el lehetne helyezni. Kövessük a számítógépet a második megoldás végrehajtása alatt! 1. Lépés az operátorra (1. sor). Y = 5; // 1. sor 2. Tájékozódik, hogy mi van az operátor jobb oldalán (a jobb oldali rész az egyenlőségjel és a pontosvessző között helyezkedik el). 3. A számítógép érzékelte, hogy az operátor jobb oldali része egy számszerű értéket tartalmaz. 4. Felveszi ezt a számszerű érték (5) az Y változó memóriacellájában. 5. A következő operátorra lép (2. sor). X = Y - 2; // Line 2 6. Megvizsgálja az operátor jobb oldalát. 7. A számítógép érzékelte, hogy az operátor jobb oldala egy kifejezést tartalmaz. 8. Kiszámolja az operátor jobb oldalának a számszerű értékét (5-2). 9. Ezt a számszerű értéket (3) rögzíti az Х változó memóriacellájában. Az 1-4 lépésekben a számítógép az első operátort hajtja végre (1. sor). A számítógép az 5-9 lépések a második operátort hajtja végre (2. sor). Azért hogy egy működőképes programot kódoljon, a programozónak tisztában kell lennie azzal, hogy hogyan fogják végrehajtani a műveleteket ebben a programban. Nem minden matematikai számítást lehet részletesen egy programba beletenni, néha szükség van az operátorok előkészítésére. Például sok közbenső számítást végeznek a matematikai feladatok megoldása során. Segíthet egy matematikus, hogy megtaláljon egy megfelelő megoldást, de kiderülhet, hogy ez haszontalan a programozás szempontjából. Csak értelmes feladatokat bízzunk a programra: adjuk meg a változók eredeti értékeit és a képletet a másik változó értékének kiszámításához. Az előző példában az első operátor az Y változó számszerű értékével kapcsolatos információt képviseli, és a második operátor nyújtja a képletet, hogy hogyan számítsa ki az X változó értékét, ami iránt érdeklődünk

38 Minden működőképes program a kifejezések ismerős látványát tartalmazza. Ha ilyen kifejezéseket találsz, el tudod dönteni, hogy lehetséges-e, hogy ezek a kifejezések operátorokban szerepeljenek a programodban. Például, a lenti rekord: X = X + 1; // Ez egy számláló Úgy tűnik, hogy hibás a matematikai logika és józan ész szempontjából. Mindazonáltal, ez elfogadható, ha operátorként vizsgálod (mellesleg ezt az operátort széles körben használják a kódolásban). Ebben az operátorban kiszámítjuk az X változó egy új értékét. Mikor végrehajtjuk az értékadó operátort, (vagyis kiszámoljuk az operátor jobb oldalának az értékét), a számítógép érzékeli azt, hogy egy memóriacella tartalmazza X változó számszerű értékét, (például kiderül, hogy ez pillanatnyilag egyenlő 3- mal), kiszámítja a kifejezést az operátor jobb oldalán (3 + 1), és a kapott új értéket (4) írja X változó részére fenntartott memóriacellába. Az értékadó operátor végrehajtása azt eredményezi, hogy az X változó új értéket kap (4). A gép tárolni fogja X változó ezen értékét, addig, amíg egy másik értékadó operátor meg nem változtatja azt. Ebben az esetben ennek a változónak a kiszámított az új értéke lesz tárolva a következő változásig

39 Függvények A függvény meghatározása A legfontosabb technológiai áttörés a számítógépekben az olyan különálló kódrészek létrehozásának és a raktározásának a lehetősége, amelyek leírják az adatfeldolgozás szabályait vagy megoldanak egy problémát vagy annak egy kis részét. Ez a lehetőség az MQL4-ben szintén létezik. A függvény a neve annak a speciális programrésznek, ami leírja az adatátalakítás módszerét. Miközben függvényről beszélünk, két összetevővel fogunk találkozni:a függvényleírással és a függvényhívással. Függvényleírásnak egy program speciális végrehajtható részét nevezzük. Függvényhívás (függvényreferencia) egy olyan rekord, aminek a végrehajtása egy függvény végrehajtásával végződik. Mindennapos életünkben a függvénynek sok analógiáját találhatjuk. Vegyük például egy autó fékrendszerét. Az egész szerkezetnek, -ami a mérnök ötleteként megvalósítja a fékezést - egy függvény az analógiája, míg a fékpedál a függvényhívás analógiája. A vezető lenyomja a pedált, és a működésbe hozott szerkezetek végrehajtanak néhány műveletet és leállítják az autót. Ugyanígy, ha egy függvényhívás történik egy programban, a hívott függvény végre fogják hajtani, vagyis egy számítást vagy egyéb műveletet fognak végrehajtani (például egy üzenet megjeleníteni vagy elküldeni egy megbízást, stb.). Egy függvénynek az az értelme, hogy a program alapvető szövegén kívülre viszi a kód egy logikailag összefüggő részét, és csak az ennek a részének szóló hívás marad a program alap szövegében. Az ilyen programszerkezetnek van néhány kétségbevonhatatlan előnye: először, azt a programszöveget, amit ilyen módon alkottak sokkal könnyebb olvasni; másodszor, könnyű átlátni és, ha szükséges, módosítani egy függvény szövegét anélkül, hogy a program alapvető kódjában bármilyen változást csinálnánk; harmadszor, egy függvényt egyetlen fájlként alkothatunk és használhatunk, és azt, ha szükséges a programozó egy másik programba beillesztheti, és az új program részeként alkalmazhatja. Azt mondhatjuk, hogy az MQL4 programokban levő kód legnagyobb részét már megírták függvényalakban. Egy függvény létrehozása Egy függvényt leírhatnak és hívhatnak. Figyeljünk meg egy példát! Tegyük fel, hogy nekünk van egy kicsi programunk (18. ábra) amelyik kiszámolja az átfogó hosszát, a derékszögű háromszög befogóiból a Pythagoras tétel használatával. Ebben a programban minden számítást egy helyre lokalizáltak, az operátorokat végrehajtják egytől egyig, mint ahogyan programban történik (fentről lefelé)

40 18. ábra. Az egyszerű program kódja pifagor.mq4. 8. feladat: Alkosd meg az adott programkód egy részét függvényként. Ésszerű volna létrehozni egy függvényt, amely azt a két sort használja, ami a keresett értéket megtalálja. Az a program, ami egy függvényt használ látható a 19. ábrán. Ebben a programban a számítások egy részét függvényként alkották meg. Az alapkód tartalmaz egy felhasználói függvényhívást. A felhasználói függvény leírását az alapkódon kívül lokalizálják (az alapkód után). 19. ábra. Egy olyan program kódja, amely a felhasználói függvény leírását, és e függvény hívását is tartalmazza gipo.mq

41 A program mindkét verziója ugyanazt az eredményt fogja adni. Míg a kódot az első verzióban egyetlen modulként alkotjuk (18. ábra), ellenben a második verzióban (19. ábra) a számítások egy részét egy függvényben hajtjuk végre, amit az alapszövegből hívtunk. A különálló függvényben végrehajtott számítások befejezése után a számítások a fő kódban folytatódnak. A függvény végrehajtásához nekünk hívnunk kell azt (hivatkozni kell rá). Ez az oka annak, hogy a függvény gyakorlatilag két részből áll: maga a kód, ami a függvényt alkotja (függvényleírás) és a függvényhívás, ami elindítja a függvényt (utalás a függvényre). Ha nem hívod a függvényt, az nem hajtódik végre. Ugyanakkor, ha hívsz egy nem létező függvényt, az eredménytelen lesz (a MetaEditior egy hibaüzenetet fog adni, ha megpróbálsz lefordítani egy ilyen MQL4 programot). Függvényleírás Egy függvény leírása két alapvető részből áll - függvényfejléc és függvénytörzs. Egy függvény fejléce a visszaadott érték típusából, a függvény nevéből és a formális paraméterek listájából áll. A formális paramétereket listáját zárójelbe tesszük és a függvénynév után írjuk. A visszaadott érték típusa az egyik korábban ismertetett típus lehet: int, double, bool, color, datetime vagy string. Ha a függvény nem küld vissza értéket, a típusa void (tartalom nélkül), vagy bármilyen típust írhatunk. Egy függvény törzsét kapcsos zárójellel nyitják. A függvénytörzs tartalmazhat egyszerű és/vagy összetett operátorokat és hívhat egyéb függvényeket. A függvény visszatérési értéke, a return() operátor zárójelei között szerepel. A visszatérési érték típusának, amit a return() operátor visszaküld, illeszkedni kell a függvényfejlécben megadott típussal. A függvény leírását kapcsos zárójelek fejezik be. 20. ábra. Függvényleírás. A függvényleírást külön kell lokalizálni a programban, kívül minden egyéb függvényen (vagyis nem egy függvényen belül, hanem ezen kívül). Függvényhívás Függvényhívás a függvény nevét és az átvitt paraméterek listáját jelenti. Az átvitt paraméterek listáját zárójelbe teszik. A függvényhívást egy különálló operátornak vagy egy operátor részeként tüntethetjük fel. 21. ábra. Függvényhívás (utalás egy függvényre)

42 . Minden függvényhívás mindig hivatkozik egy függvényre (nem minden függvényre, az összes közül csak egyre). A függvény típusok Három fajta függvény van - különleges függvények, standard (beépített, előre definiált) függvények, és felhasználói függvények. Különleges (speciális) függvények Az MQL4-ben összességében 3 különleges függvény van. Ezeknek előre definiált nevük van: init(), start(), és deinit(), ezeket a neveket nem használhatjuk semmilyen másik függvény neveként. A különleges függvények részletes vizsgálatát a Különleges (speciális) függvények fejezetben találjuk. Itt csak megemlítjük, hogy egy program alapvető kódja ezekben a függvényekben helyezkedik el (18, 19. ábra). A különleges függvények tulajdonsága, hogy őket az ügyfélterminál hívja, azért hogy ezeket végrehajtsa. Bár a különleges függvényeknek megvan a függvények minden általános tulajdonsága, őket általában nem hívjuk a programból, ha ezt a programot megfelelően kódoltuk. Beépített (standard) függvények Az MQL4 nem szűkölködik a beépített függvényekben, amelyeket a programok kódolás közben használhatunk. Például: négyzetgyököt számol, üzeneteket ír a rendszernaplóba vagy a képernyőre. A beépített függvényeket az előre definiált algoritmusuk szerint hajtja végre a program. A felhasználónak meg kell tanulnia ezeknek a függvényeknek a működését. Biztos lehet benne, hogy minden beépített függvényt megfelelően írtak meg a szakértők a lehetséges legjobb algoritmus szerint. A beépített függvények sajátos jegye az, hogy őket nem írjuk le a program szövegében. A beépített függvényeket ugyanúgy hívjuk meg a programban, mint bármilyen más függvényt (ez az általános gyakorlat). A példáinkban (18. és 19. ábra), két általános függvényt használtunk: MathSqrt() és Alert(). Az első feladata, hogy négyzetgyököt számoljon, míg a második feladata az üzenetközvetítés, a zárójelben lévő szöveg írja ki a képernyőre. A beépített függvények tulajdonságait a Beépített (standart) függvények fejezet bekezdéseiben részletesen tárgyalni fogjuk. Észre fogjuk venni, hogy itt csak függvényhívásokat találunk, míg ezeknek a függvényeknek a leírását nem találjuk a programban. A beépített függvényeket nevezhetjük még általános, standard vagy előre definiált függvényeknek. Ezek közül a meghatározások közül bármelyiket használhatjuk. Felhasználói függvények Néhány esetben a programozók saját függvényeket írhatnak és használhatnak. Felhasználói függvényeknek nevezzük ezeket a függvényeket. Felhasználói függvények esetén a programokban szerepelni kell a függvényleírásnak és függvényhívásnak is

43 1. táblázat. Függvényleírás és függvényhívás, a különböző típusú függvények használata a programokban. Függvénytípus Függvényleírás Függvényhívás Speciális (különleges) Alkalmazható Nem alkalmazható (*) Standard (beépített) Nem alkalmazható Alkalmazható Felhasználói Alkalmazható Alkalmazható (*) Bár különleges függvényeket technikailag hívhatunk egy programban, nem ajánlott ezt tenni. A függvények tulajdonságai Függvényvégrehajtás Minden függvény fő tulajdonsága az, hogy a hívott függvényeket végrehajtják. A függvényeket a kódjaik szerint hajtják végre. Átadott paraméterek és visszatérési érték Az átadott és visszakapott információ vonatkozásában, egy függvény olyan, mint egy általános számológép. Beírsz (a billentyűzettel) egy olyan kifejezést, ami több értékből áll, és válaszként egy értéket fogsz kapni. Egy függvény egy vagy több paramétert dolgoz fel abból a programból ami hívta ezt a függvényt. A függvény el fogja végezni a műveletet, és visszatér erre a programra a paraméterekre adott válasszal. Az átadott paramétereket a függvény neve után zárójelbe téve részletezzük. Az átadott paramétereket vesszőkkel választjuk el. Az átadott paraméterek száma nem haladhatja meg a 64-et. A függvényhívás lehetséges akkor is, ha nem szerepelnek benne átadott paraméterek. Ebben az esetben a paraméterek üres listáját adjuk meg, vagyis közvetlenül a függvény neve egy nyitó és egy záró zárójelet teszünk. A függvényhívásban levő átadott paraméterek számának, típusainak és sorrendjének meg kell egyeznie a formális paraméterekkel, amelyeket a függvényleírásban megadtunk. (Az olyan függvénynek szóló hívás, amely függvénynek alapértelmezett paraméterei vannak kivételt képez - lásd: Függvényhívás és Függvényleírás és a "return" operátor). Ha nem illeszkednek a paraméterek, a MetaEditor egy hibaüzenetet fog adni. Állandókat, változókat, kifejezéseket és tömböket használhatunk átadott paraméterekként. A visszatérési értéket a return() operátor zárójelei között adjuk meg (lásd: Függvényleírás és a "return" operátor). A return() operátor által visszaküldött érték típusának illeszkednie kell a függvényfejlécben megadott típushoz. Az is lehetséges, hogy egy függvény nem küld vissza értéket. Ebben az esetben semmit nem írunk return() operátor zárójeleibe. A példánkban az átadott paraméterek A és B változók (21. ábra), míg a visszaadott érték C változó (20. ábra). Az átadott paraméterek és a formál paraméterek megfeleltetését láthatjuk a 22. ábrán. A függvényhívásban levő átadott paraméterek számának, típusainak és sorrendjének össze kell illeniük a formális paraméterekkel, amelyeket a függvényleírásban megadtak. A függvényleírás fejlécében formális paraméterként csak változókat használunk

44 22. ábra. Illeszkedik az átadott és a formális paraméterek száma, típusa és sorrendje. Egyedül változókat használtunk átadott paraméterekként. Változókat, kifejezéseket és állandókat is használhatunk átadott paraméterekként. 23. ábra. Illeszkedik az átadott és formális paraméterek száma, típusa és sorrendje. Egy állandót, egy kifejezést, és a megfelelő típusú változókat használjuk átadott paraméterekként. Formális paraméterek A formális paraméterek használata függvények egy fontos tulajdonsága. A formális paraméterek a változók egy listája, amelyeket a függvényleírás fejlécében részleteznek. Mint már ezelőtt említettük, hogy egy függvényt több programban használhatnak. Azonban a különböző programok különböző változó neveket használnak. Ha a változók neveinek szigorú összeillesztést követelnénk meg a függvények használata során, ez nem lenne kényelmes a programozóknak. Valójában ekkor minden újonnan fejlesztett programban olyan változó neveket kellene használni, amiket már azelőtt a függvényekben használtak. Azonban szerencsére a függvényekben használt változók neveinek nincs kapcsolatuk a programban használt változók neveivel. Nézzük a 20. és 21. ábrát még egyszer! Vegyük észre, hogy az átadott paraméterek nevei (A és B a függvényhívás zárójeleiben) nem eggyeznek a függvény leírásban részletezett paraméterek neveivel

45 (a és b)! Mint azt az Állandók és változók bekezdésben tárgyaltuk MQL4 nagybetű érzékeny. Így, a szóban forgó nevek A és a, B és b itt különböző változók nevei. Mindazonáltal ebben a kódban nincs hiba. A függvényleírásban lévő változók (formális paraméterek) nem kapcsolatosak a változókkal, amiket a program alapvető kódjában használtak. Ezek különböző változók. A változókat, de nem az állandókat, formális paraméterekként meg lehet adni a függvény fejlécben. Hogyan működik A programban, történik egy függvényhívás, A és B változókat részletezik a zárójelekben. A program hívja a neve alapján a függvényt, aminek formális paraméterei vannak, a és b a fejlécében részletezve. Az A változó értékét adja a változónak. A B változó értékét adja b változónak. Végrehajtja a függvény a számításokat, a és b változók értékeit használva. Adhatunk a formális paramétereknek bármilyen nevet (ezek nem kell, hogy egybe essenek a változók neveivel, amiket a programban használtak). Ebben a példában a formális paraméterek azonosítására a-t és b-t használtuk. Használhatnánk bármi mást is például: m és n, vagy Kat_1 és Kat_2. Persze, mikor írod a programot ( a függvényt), a függvény törzsben a számítások során azokat a változó neveket kell használni, amely változó neveket a fejlécben megadtál. Ha a fejlécben a és b szerepel, akkor a függvénytörzsben is a-t és b-t kell használni és nem m-et és n-t. Egy függvényben a számításokat az a és b formális paraméterekkel végzik és nem az A és B változókkal. Egy függvény, bármilyen műveletet végrehajthat az a és b formális paramétereken (az ezeknek a változóknak az értékeiben bekövetkező változtatásokat is beleértve). Azonban nincs hatása az A és B változókra. A függvényben kiszámolt értéket visszakapjuk a return() operátor zárójeleiben. Egy változó értékét, egy kifejezés eredményét, vagy egy állandót visszaadott értékként használhatunk. Az esetünkben ez a c lokális változó értéke (lokális változó: egy változót egy függvényen belül deklarálnak; ha elhagyjuk a függvényt, minden lokális változó értéke el fog veszni; további részletet a lokális változókról A változók típusai részben fogunk találni). A függvény visszaadja a programnak a c lokális változó értékét (19. ábra). Ez azt jelenti, hogy ezt az értéket fogja kapni a C változó. További számításokat végezhetünk a programban, ha szükséges, azokkal a változókkal, amelyeket a meghívott függvényen belül deklaráltak. Az esetünkben a hívó függvény a különleges start() függvény (ez tartalmazza azt sort, amely hívja a felhasználói függvényt), a hívott függvényen belül deklaráljuk az A, B, és C változókat. Ennél fogva, a függvényben a számításokat a formális paraméterekkel hajtjuk végre, melyek lehetővé teszik, hogy olyan függvényeket hozzunk létre, amelyek önkényes változóneveket használnak, függetlenül azoktól a változónevektől, amiket ténylegesen a fő programkódban használunk. Példa: beépített függvény egy programban Először is vizsgáljuk meg a 18. ábrán látható program viselkedését. Meg kell jegyezni, hogy a program egész kódja a start() különleges függvényben helyezkedik el. Ennél a tanulási fázisnál nem fogunk különösen figyelni erre (a különleges függvényekről a Különleges Függvények fejezetben részletesen beszélünk). Kövessük a program végrehajtását, ami egy értékadó operátorral kezdődik: int A = 3; // Első befogó 1. Az értékadó operátor jobb oldala tartalmazza az állandót, az értéke A 3 értéket (a jobb oldali rész értéke) megkapja az A változó (az egyenlőségjel bal oldala az értékadó operátorban). A vezérlést a következő sor kapja: int B = 4; // Második befogó 3. Az értékadó operátor jobb oldala tartalmazza az állandót, az értéke

46 4. 4 értéket kapja a B változó. A program a következő sort végrehajtásába fog: int C_2 = A*A + B*B; // A befogók négyzeteinek összege 5. Az értékadó operátor jobb oldalának a kiszámítása. A számítások eredménye a 25-ös érték (azt a módszert, hogy egy program hogyan használja a változókat, hogy az értéküket megkapja az Állandók és változók részben fogjuk tárgyalni, úgyhogy itt most ezt nem részletezzük). 6. A C_2 változó a 25 értéket kapja A következő sor tartalmaz egy értékadó operátort, aminek a jobb oldalán egy beépített függvénynek szóló hívás van: int C = MathSqrt( C_2); // Az átfogó kiszámítása A program arra törekszik, hogy végrehajtsa az értékadó operátort. Ezért először az egyenlőségjel jobb oldalán levő számításokat hajtja végre. 7. A program végrehajtást követel a MathSqrt() beépített függvénytől (ami négyzetgyökök számol). A C_2 változó értéke (most ez az érték egyenlő 25-tel) átvitt paraméterként fog szerepelni. Vedd észre, hogy ennek a beépített függvénynek a leírása nincs sehol a programban. A beépített függvények leírásainak nem kell a programokban elhelyezkedniük. Egy program szövegében könnyű megkülönböztetni a beépített függvényhívást: ezeket sötétkékkel emelik ki a MetaEditor (a programozó önkényesen megválaszthatja a színeket ). 8. A számításokat elvégzi az MathSqrt() beépített függvény. 9. A MathSqrt() beépített függvény befejezte a számításait, és visszaküldi a kiszámított értéket. Az esetünkben ez az 5 (a 25 négyzetgyöke). Az az érték, amit visszaküldött a függvény lesz most a rekord tartalma: MathSqrt( C_2) Ezt a rekordot felfoghatjuk egy különleges, összetett változóként, egy fajta dologként, amiben a számításokat végrehajtják. Miután ezeket a számításokat befejezték, ez a dolog felvesz egy értéket. Meghatározó itt az a tény, hogy ezt az értéket a függvény küldte vissza. Lehet ez az érték egy másik változó értéke vagy felhasználhatjuk bármilyen másik számításban. 10. Ebben az esetben az értékünk az értékadó operátor jobb oldalának az értéke. Az értékadó operátor végrehajtása során a program a C változó értéknek 5-öt fog adni. 11. A következő sorban található az az operátor, amelyik hivatkozik az Alert() beépített függvényre (függvényhívás). Alert("Az átfogó = ", C); // Üzenet a képernyőn Az Alert()beépített függvény kinyit egy olyan üzenet dobozt, ahol ennek a függvénynek adott paramétereket láthatjuk. Ebben az esetben a függvény átadott paramétere két értéket tartalmaz: - a string állandó értékét: Az átfogó = - a C változó szám értékét: 5 Ismert, hogy nem minden függvények kell visszaküldenie az értékét (a függvényvégrehajtás eredményét). Az Alert() beépített függvény nem küld vissza értéket, mert ennek más feladata van ennek egy szöveget kell mutatnia a képernyőn egy külön ablakban. Az Alert(), beépített függvény végrehajtásának eredményeként ennek a függvénynek az ablakában a következő sor fog megjelenni: Az átfogó = Az utolsó operátor ebben a programban befejezi a különleges start() függvény munkáját

47 return; // Kilépés operátor Ennél a pontnál a program munkája kész. Egy kérdés felmerülhet: Hogyan fogjuk tudni, hogy kapunk-e visszatérési értéket, vagy nem? A válasz erre a kérdésre nyilvánvaló: A beépített függvények részletes leírását megtalálod, ha elovasod a program documentációt vagy tájékozódsz a MQL4.community és a MetaQuotes Software Corp. oldalakon és a MetaEditor Help-jében. Most egy felhasználói függvény tulajdonságait részletezzük. Hogy egy felhasználói függvény visszaküldi az értéket vagy nem, az az algoritmusától függ (a döntést a programozó hozza a függvény programkódjának az írása során). Példa: felhasználói függvény egy programban Figyeljük meg, hogy milyen számításokat végeznek abban a programban, ami egy felhasználói függvényt tartalmaz (19. ábra). A kód egy bizonyos része, amit korábban megtaláltunk a különleges start() függvényben most hiányzik. Azt lecserélték a felhasználói függvénynek szóló hívásra. Mindazonáltal a start() különleges függvény követi a felhasználói függvény leírását. Az első két sor, amiben A és B egész számváltozók számszerű értékeket vesznek, ugyanaz marad. Következetesen semmi nem változik a végrehajtásukban: int A = 3; // Elsö befogó int B = 4; // Második befogó A harmadik sorban van nálunk egy értékadó operátor. A jobb oldala tartalmazza a felhasználói függvénynek szóló hívást: int C = Gipo(A,B); // Az átfogó kiszámítása 6. Miközben végrehajtja ezt az operátort, a program először is hívni fogja a felhasználói függvényt. Megjegyzés: A felhasználói függvény leírásának jelen kell lennie a programodban és a helye közvetlenül a különleges start() függvényt bezáró kapcsos zárójel után van (vagyis a (start) különleges függvényen kívül). Miközben hívja a felhasználói függvényt, a program végre fogja hajtani a következőt: 6.1. Hivatkozik az A változóra, hogy megkapja az értékét, ( 3) 6.2. Hivatkozik az B változóra, hogy megkapja az értékét, (most ez 4) Megjegyzés: Amint a program elkezdi hívni a felhasználói függvényt (a felhasználói függvény nem speciális eset; ez a szabály minden függvényre vonatkozik), a program megkapja a változók értékeinek másolatát, amiket átadott paraméterekként használ, de ezeknek a változóknak az értékei (ebben az esetben, A és B) a felhasználói függvény alkalmazása miatt nem változnak. 7. A vezérlést megkapja a felhasználói függvény. A felhasználói függvény végrehajtásának a teljes ideje alatt (függetlenül attól, hogy ez milyen hosszú), a hívó programban levő változók az értékeiket nem fogják elveszteni, hanem tárolni fogják azokat. Az első sor a felhasználói függvény leírásában a fejléc: int Gipo(int a, int b) // Felhasználói függvény A felhasználói függvény végrehajtása alatt, a program a következőket fogja tenni: 7.1. A 3 értéket (az első érték az átadott paraméterek listájában) a változónak fogja kijelölni (az első változó a formális paraméterek listájában)

48 7.2. A 4 értéket (a második érték az átadott paraméterek listájában) b változónak fogja kijelölni (a második változó a formális paraméterek listájában). Azután a vezérlést meg fogja kapni a függvénytörzs azért, hogy végrehajtsa az algoritmusát. Az első operátor a függvénytörzsben: int c2 = a*a + b*b; // A befogók nényzeteinek összege 7.3. Miközben végrehajtja ezt az operátort, a program kiszámítja az értékadó operátor jobb oldalán lévő értéket, és az így kapott érték lesz a c2 változó értéke (jelenleg ez 3*3 + 4*4 = 25). A következő operátor: int c = MathSqrt(c2); // Az átfogó 7.4. Itt keressük a c2 változó értékének a négyzetgyökét. A művelet ugyanaz, mint az előző példában. A beépített függvény leírását sehol nem kell megadni. Az értékadó operátor végrehajtása azzal fog végződni, hogy a c változó 5 értéket fog kapni A következő sorban operátor van: return(c); // Függvény kilépési (visszatérési) operátor Miközben a program végrehajtja ezt az operátort, a program vissza fogja adni (a felhasználói függvény hívás oldalára) azon változó értékét, ami ennek az operátornak a zárójeleiben van. Jelen esetben a c változó értéke 5. Ennél a pontnál a felhasználói függvény végrehajtásának vége van, az irányítást a hívás oldalnak adják. 8. Visszatérés oda, ahonnan a felhasználói függvényt hívta az operátor. A int C = Gipo(A,B); // Átfogó kiszámítása Gipo(A,B) rekord (a felhasználói függvényhívás) a visszatérő érték, felvesz azt az értéket, amit a függvényben kiszámoltunk (esetünkben értéke 5). Miközben befejezi az értékadó operátor végrehajtását, a program a C változónak az 5 értékét fogja adni. 9. A következő operátort Alert("Az átfogó = ", C); // Üzenet a képernyőn a program ugyanúgy fogja végrehajtani, ahogy az előző példában, mégpedig: egy külön ablakban fog az üzenet megjelenni: Az átfogó = Az utolsó operátor ebben a programban a return; // Függvény kilépési operátor befejezi a start(), különleges függvény munkáját és ezzel egyidejűleg befejezi az egész program munkáját (a különleges függvények tulajdonságait a Különleges függvények) részben fogjuk tárgyalni). Lássunk néhány variációt a fenti felhasználói függvény megvalósítására! Könnyű belátni, hogy a programozás felhasználói függvényekkel való megvalósításának van néhány kétségbe vonhatatlan előnye

49 A korábban megismert Gipo() felhasználói függvény megvalósítása Ebben a függvényben a formális paraméterek hasonlítanak azokra a változókra, amiket az alap programban használtak. Azonban ez csak egy látszólagos hasonlóság, mert A és akülönböző változók nevei. // int Gipo(int a, int b) // Felhasználói függvény int c2 = a*a + b*b; // A befogók négyzeteinek összege int c = MathSqrt(c2); // Átfogó return(c); // Függvény kilépési operátor // A Gipo() felhasználói függvény megvalósítása: 2. verzió Ebben az esetben a formális paraméterek nevei nem hasonlítanak azon változók neveire, amiket az alapvető programban használtunk. Ez nem akadályozza meg, hogy használjuk ezt a függvényt a programban. // int Gipo(int alpha, int betta) // Felhasználói függvény int SQRT = alpha*alpha + betta*betta; // A befogók négyzeteinek összege int GIP = MathSqrt(SQRT); // Átfogó return(gip); // Függvény kilépési operátor // A Gipo() felhasználói függvény megvalósítása: 3. verzió Ebben a példában az alfa változót újra felhasználjuk a programban és kétszer is megváltoztatja az értékét. Ennek a ténynek nincs hatása azokra a változókra, amiket a főprogram függvényhívásában megadtak. // int Gipo(int alpha, int betta) // Felhasználói függvény alpha= alpha*alpha + betta*betta; // A befogók négyzeteinek összege alpha= MathSqrt(alpha); // Átfogó return(alpha); // Függvény kilépési operátor // A Gipo() felhasználói függvény megvalósítása: 4. verzió Ebben a felhasználói függvényben minden számítást egy operátorban gyűjtenek. A visszaadott értéket közvetlenül a return() operátor zárójeleiben számolják ki. Ez a megoldás először furcsa és hibásnak tűnik. Mindazonáltal nincs hiba a felhasználói függvénynek ebben a megvalósításában. Az operátorok száma, amit ebben a függvényben használtak, kisebb mint a másik megvalósításokban tehát a kódról kiderült, hogy tömörebb. // int Gipo(int a, int b) // Felhasználói függvény return(mathsqrt(a*a + b*b)); // Függvény kilépési operátor

50 // Tehát a felhasználói függvények alkalmazásának van néhány kétségbevonhatatlan előnye a programozási gyakorlatban: a program alapszövegében levő változók neveinek nincs kapcsolata a formális paraméterek neveivel a felhasználói függvényben; a felhasználói függvények különböző programokban újra használhatók, nem szükséges megváltoztatni a felhasználói függvény kódját; ha szükséges függvénykönyvtárakat hozhatunk létre. A függvényeknek ezek a nagyon hasznos tulajdonságai teszik lehetővé egy vállalatnál az igazán nagy programok létrehozását. Több programozó tud egyidejűleg résztvenni ebben a folyamatban, nem kell egyeztetniük a felhasznált változók neveit. Az egyetlen dolog, amit egyeztetniük kell az a változók sorrendje a függvényfejlécben és a függvényhívásban

51 A program típusok Mikor elkezi egy MQL4 program írását, a programozónak először is körülbelüli választ kell adnia arra a kérdésre, hogy milyen lesz ez a program. A program tartalma és funkcionalitása teljesen ettől függenek. Az MQL4 alatt 3 fajta program írható: Expert Advisorok, scriptek, és egyéni indikátorok. Bármilyen program, amit a programozó készítet, ezek közül a típusok közül az egyikhez fog tartozni. Ezeknek különböző céljaik és sajátos tulajdonságaik vannak. Részletezzük ezeket a típusokat! Expert Advisor (EA) egy MQL4-ben kódolt program, amit az ügyfélterminál minden ticknél futtat. Az Expert Advisor fő célja a kereskedés programozott irányítása. Az Expert Advisort a felhasználók kódolják. Nincs az ügyfélterminálban beépített EA. A script egy olyan MQL4 program, amit az ügyfélterminál csak egyszer hajt végre. A scriptek feladata az olyan műveletek végrehajtása, amiket csak egyszer kell elvégezni. A scripteket a felhasználók kódolják. Ezeket sem szállítják az ügyfélterminállal beépített programokként. Egyéni indikátor egy MQL4 kódú program, amit az ügyfélterminál indít el és hajt végre minden ticknél. Az alapvető feladata előzetesen meghatározott összefüggések grafikus bemutatása. Az indikátorok nem tudnak kereskedni. Két fajta indikátor van: technikai (beépített) indikátorok és egyéni indikátorok. Az indikátorokat tárgyalni fogjuk az A technikai indikátorok használata és a Az egyéni indikátorok létrehozása részekben. A programozó attól függően választja ki a program fajtáját, hogy mi lesz ennek a programnak a célja, mert a különböző programtípusoknak eltérő tulajdonságaik és a korlátozásaik vannak. A programok tulajdonságai A programvégrehajtás elindítása Van egy olyan ismertetőjel, ami megkülönbözteti az Expert Advisort és egyéni indikátorokat a scriptektől. Ez a futástartamuk. A Néhány alapfogalom részben megemlítettük már hogy a programok működési ideje a tickek sokszorosa. Ez a kijelentést igaz EA-kra és az egyéni indikátorokra, de ez az állítás a scriptekre hamis. Expert Advisor és egyéni indikátor. Amint hozzácsatoltál egy programot (EA vagy az egyéni indikátor) a charthoz a szimbólumablakban a program végrehajt néhány előkészítő műveletet és várja, hogy egy tick érkezzen. Amint egy új tick jön, a programot el fogja indítani az ügyfélterminál, azután ez elvégez minden szükséges műveletet, amit előírt az algoritmusa, és végül átadja az irányítást az ügyfélterminálnak, vagyis várja a következő ticket. Ha egy új tick jön, még mielőtt a program befejeződne, ez nincs hatással a programvégrehajtásra - a program továbbra is az algoritmusa szerint működik és csak befejezése után adja át az irányítást az ügyfélterminálnak. Ezért a tickek közül nem mindegyik végződik azzal, hogy elindít egy EA-t vagy egy egyéni indikátort, csak azok, amelyek akkor érkeznek, amikor az irányítás az ügyfélterminálnál van, és a program várakozik. Az új tick elindítja a programot. Így egy Expert Advisor vagy egy egyéni indikátor hosszú időszakon keresztül működik miután csatolták a szimbólumablakhoz és időnként lefut (az érkező tickek hatására). Ezenkívül egy Expert Advisor a végrehajtás sorrendjében különbözik egy indikátortól a program első indításánál. Ezt a különbséget meghatározzák a különleges függvények speciális tulajdonságai egy bizonyos típus programban (lásd Különleges függvények). Ha egyszer csatoltak a szimbólumablakhoz egy Expert Advisort, ez elvégzi a szükséges előkészületeket (init() függvény) és készenlétbe helyezi magát, várja az első tick megérkezését amikor lefut a start() függvény. Az EA-tól eltérően az egyéni indikátor mindkét függvényt (init() és start()függvényeket) végrehajtja azért, hogy elvégezze az indikátorértékek első, előzetes kiszámítását. Később, az új tickeknél mindkét program már csak a start() függvényt hajtja végre az algoritmusa szerint. Scriptek. Az Expert Advisortól vagy az indikátoroktól eltérően, egy script végrehajtása el fog indulni az új tickre való várakozás nélkül, közvetlenül azután miután hozzácsatolták egy szimbólumablakhoz. A script egész kódját pontosan végre fogja hajtani. Miután minden programsort végrehajtott, a script befejezi a műveleteit és eltávolításra kerül a szimbólumablakból. Scriptet használhatunk, ha nyitni vagy zárni akarunk egy megbízást, szöveget akarunk a képernyőre írni vagy grafikus objektumokat akarunk megjeleníteni stb

52 Az Expert Advisorok scriptek és indikátorok végrehajtásában lévő egyéb különbségeket, a különleges függvényeik tulajdonságait a Különleges függvények részben fogjuk alaposabban tárgyalni. Kereskedés Az egyik fő ismertetőjel, ami a fenti programokat jellemzi az a tulajdonságuk, hogy kereskedelmi utasításokat adhatnak. Egy kereskedelmi utasítás egy olyan parancs, amit egy program ad a kereskedő szervernek, hogy az nyisson, zárjon, vagy módosítson egy megbízást. Kereskedelmi utasításokra a programokban beépített függvényeket használunk, ezeket a függvényeket nevezzük kereskedelmi függvényeknek. Csupán az Expert Advisoroknak és a scripteknek van a joguk ahhoz, hogy kereskedő függvényeket használjanak (csak ha ezt a megfelelő opciót lehetővé teszik az EA/script beállításában). Tilos egyéni indikátorokban használni a kereskedő függvényeket. Egyidejű használat A különböző típusú programok abban is különböznek egymástól, hogy egyidejűleg mennyit lehet belőlük csatolni egy szimbólumablakhoz. Expert Advisor. Egy szimbólumablakhoz csak egy EA-t lehet hozzácsatolni; több Expert Advisors egyidejű használata tilos. Script. Egy szimbólumablakhoz egyidejűleg csak egy scriptet lehet csatolni; több script egyidejű használata tilos. Egyéni indikátor. Egyidejűleg egy szimbólumablakhoz több indikátort is lehet csatolni, nem fogják zavarni egymást. Minden programtípust elindíthatunk egyidejűleg egy szimbólumablakban a típusra vonatkozó korlátozásokkal. Például el tudsz indítani egy EA-t, ugyanabban az ablakban egy scripet és különféle indikátorokat, vagy egy EA-t és egy indikátort. Ellenben nem indíthatsz el különféle EA-kat vagy scripteket. Ugyanakkor egyidejűleg elindíthatsz ugyanolyan típusú programokat egy szimbólum különböző ablakaiban. Például, ha két Expert Advisor-t akarsz elindítani egy szimbólumon, el tudsz indítani ennek a szimbólumnak az egyik ablakában egy EA-t és egy másikat ugyanazon szimbólum egy másik ablakában. Ebben az esetben az Expert Advisorok egyidejűleg fognak dolgozni. Azonban figyelembe kell venned azt, hogy az EA-k egymásnak ellentmondó kereskedő utasításokat adhatnak. Például az egyik nyit egy megbízást, míg a másik utasítást adhat a bezárására. Ez fölösleges kereskedések sorozatához vezethet és lehet, hogy teljes veszteséggel végződik. Minden programtípus, amiket elindítunk az ügyfélterminálban, köztük azok is, amik különböző szimbólumok ablakaiban futnak, lehetővé teszik, hogy globális változókat hozzunk létre. Ez lehetővé teszi a gépnek, hogy koordinálja a programok egyidejű operációit. Annak a módját, hogy a globális változókat hogyan használjuk, megtárgyaljuk a Globális változók részben. A programok elindítása Minden típusú programot csak a felhasználó indíthat el. Az MQL4-ben nem tudsz hívni egy Expert Advisort, egy scriptet, vagy egy indikátort programozott végrehajtással. Az egyetlen kivétel az icustom() függvény, ami lehetővé teszi, hogy adatot kérj egy egyéni indikátortól, és technikai indikátorok függvényei ugyanezen célból. Az icustom() függvény hívása vagy a technikai indikátorok függvényei nem teszik lehetővé, hogy a hívott indikátor a képernyőre rajzoljon (lásd:egyszerű MQL4 programok)

53 2. táblázat: Expert Advisorok, scriptek, és egyéni indikátorok fő tulajdonságai A program tulajdonsága Expert Advisor Script Indikátor Futási idő Folyamatosan Egyszer Folyamatosan Kereskedés Megengedett Megengedett Tiltott Vonalak rajzolása Nem Nem Igen Ugyanazon típus több programjának egyidejű használata Tiltott Tiltott Megengedett Végrehajtás kérése másik programtól Tiltott Tiltott Tiltott Tehát, ha egy olyan programot akarunk, ami egy bizonyos algoritmus szerint kereskedik, nekünk írnunk kell egy EA-t vagy egy scriptet. Azonban ha azt akarjuk, hogy legyen egy bizonyos információnk, ami grafikusan mutatja az eseményeket, akkor nekünk egy indikátort kell használnunk

54 Meta Editor Ebben a részben megtárgyaljuk a MetaEditorral készült programok általános szabályait. A MetaEditor egy többfunkciós speciális szerkesztő, amivel MQL4-ben irt programokat lehet létrehozni, szerkeszteni és lefordítani. A szerkesztőnek egy olyan felhasználóbarát interfésze van, ami lehetővé teszi a felhasználónak, hogy könnyen navigáljon, mikor ír és tesztel egy programot. Fájlrendszer: a MetaEditor elraktározza a MQL4 programok minden forráskódját a saját strukturált katalógusában a merevlemezen. Egy MQL4 program helyét meghatározza a feladata: hogy script, Expert Advisor, indikátor, include-file vagy library. Programok létrehozása és használata Nagyon könnyű létrehozni egy MQL4 programot - a beépített eszközök segíteni fognak neked. Tudod módosítani scripteket, indikátorokat vagy Expert Advisor sablonokat. Az elkészült kódot automatikusan menteni a MetaEditor a fájlrendszer megfelelő mappájába

55 Fájlrendszer Az ügyfélterminál a különböző programtípusokat az alapján azonosítja hogy melyik almappában találhatók. Minden alkalmazási programot a ClientTerminal_folder \experts könyvtárban gyüjtünk össze. Azoknak az Expert Advisoroknak, scripteknek és egyéni indikátoroknak, amelyeket a kereskedő a gyakorlatban használni fog, a megfelelő alkönyvtárakban kell elhelyezkedniük (lásd a 24. ábrát). Az Expert Advisoroknak közvetlenül a ClientTerminal_folder \expertsmappában míg a scipteknek és indikátoroknak - a ClientTerminal_folder\experts\scripts és ClientTerminal_folder \experts\indicators almappákban. 24. ábra. A felhasználó által létrehozott fájlok elhelyezkedése. A felhasználó másik elérési utat is választhat a fájljainak. Azonban azokat a kész programokat amelyek nem a megfelelő mappában helyezkednek el, az ügyfélterminál nem tudja használni. Fájltípusok Az MQL4-ben három fajta fájl van, ami programkódot tartalmaz: mq4, ex4 és mqh. Az mq4 típusú fájlok a programok forráskódjai. Minden fajta program forrásszövegét (Expert Advisors, scriptek, indikátorok) ilyen a típusú a fájlok tartalmazzák. A programkódok létrehozására a MetaEditort használjuk. Amikor egy kód teljesen vagy részben elkészült, azt elmenthetjük és későbbi módosításra újra megnyithatjuk. Az mq4 típusú fájlokat nem tudja végrehajtani az ügyfélterminál. Mielőtt elindítjuk egy program végrehajtását, azt először le kell fordítani. A forráskód-fordítás eredményeképpen, ugyanazzal a névvel egy ex4 kiterjesztéssel rendelkező fájl jön létre. Az ex4 típusú fájl egy lefordított program, ami készen áll a gyakorlati használatra az ügyfélterminálban. Ennek a típusnak a fájljait nem szerkeszthetjük. Ha egy program módosítására van szükség, akkor azt a forráskódjában (mq4 típusú fájl) kell elvégezni és azután megint le kell azt fordítani. Az ex4 állománynév nem mutatja meg, hogy milyen típusú a program - ez lehet script, Expert Advisor vagy indikátor, ex4 kiterjesztést használnak a library fájlok is

56 Az mqh típusú fájlok include fájlok. Ezeket gyakran használjuk az egyes programok forrásszövegében. Az ilyen fájlokat az Expert Advisorok, scriptek és indikátorok forrásszövegeibe lehet beilleszteni az összeállítás során. Általában ezek a fájlok tartalmazzák az importált függvények leírását (például: stdlib.mqh vagy WinUser32.mqh) vagy a közös állandók és változók leírását (stderror.mqh vagy WinUser.mqh). Az a szabály, hogy a mqh típus fájljait a ClientTerminal _folder\experts\include mappában raktározzuk el. Az include fájlokat meghívják, beillesztik a fordítás sorrán a forrásnyelvű fő állományba az #includeutasítás használatával. Annak ellenére, hogy az mqh. típusú fájlok egy programforráskódot tartalmaznak és a MetaEditorrral lefordíthatók, belőlük a lefordításuk után nem lesz önállóan végrehajtható ex4 állomány. Minden mq4 kiterjesztésű include fájlt a ClientTerminal_folder\experts\include mappában kell tárolni. Az ügyfélterminál navigátor ablakában az Expert Advisor, Egyéni Indikátorok és Scriptek szekciókban csak az olyan fájlok neveit láthatjuk, amelyeknél a kiterjesztés ex4 és a megfelelő mappában helyezkednek el. A MetaEditor régebbi verzióival szerkesztett fájlokat nem lehet elindítani, ezeket szürke szín jelöli. Olyan fájlok is előállíthatók amelyek nem teljes programok, de felhasználhatók alkalmazási programok létrehozásánál. Például egy programot több különálló fájlból hozhatunk létre vagy használhatunk egy korábban alkotott könyvtárt. Egy felhasználó létre tudja hozni az egyedi függvényeinek a könyvtárát, amiben tárolhatja a felhasználói programok gyakran használt blokkjait. Ajánlott a függvénykönyvtárakat a ClientTerminal_folder\experts\libraries mappában tárolni. Az mq4 és ex4 fájlokat használhatjuk könyvtári fájlokként. A könyvtárak nem indíthatók önmagukban. Az include fájlok használata előnyösebb mint a függvénykönytáraké, mert a könyvtári függvény-hívások több számítógéperőforrát igényelnek. A könyv első részében elemezni fogjuk az mq4 forrásszövegeket és a lefordított ex4 fájlokat

57 Programok létrehozása és használata A MetaEditor segítségével MQL4-ben írt alkalmazási programokat, Expert Advisort, scripteket és indikátorokat hozunk létre. A Meta Editor (MetaEditor.exe végrehajtható állomány) az ügyfélterminál része és a terminál gyökérkönyvtárában helyezkedik el. A Meta Editor Userguide-ját az F1 gomb hozza elő. Ez általános felvilágosítást tartalmaz, ami nélkülözhetetlen az új programok létrehozásához. A szerkesztőt meg tudod nyitni, ha a MetaEditor.exe állománynévre kattintasz, vagy az asztalon előzetesen elhelyezett parancsikon segítségével is. Az ügyfélterminál szerkezete A munka megkönnyítésére Meta Editornak beépített eszköztárai vannak: (Ctrl+D) navigátor és (Ctrl+T) szerszámosláda. 25. ábra.a Meta Editorban levő ablakok elhelyezkedése. A program szövege a szerkesztőablakban helyezkedik el, az eszköztár ablakok csak kiegészítők. A navigátornak és a szerszámosládának mozgatható határaik vannak a szerkesztőben, és az és gombokkal lehet előhozni/elrejteni őket. Egy új program létrehozása Általában egy új program létrehozás alatt a szerszámosládát és navigátorablakokat elrejtik. Így a felhasználó a figyelmét programra koncentrálhatja. Létrehozhatunk egy új programot, ha használjuk a szerkesztőmenü File>> Create opcióját vagy az új fájl létrehozása gombot. Ezek után az Expert Advisor Wizard fel fogj ajánlania a programtípusok listáját, hogy ki tudd választani a létrehozandó fájl típusát:

58 26. ábra. A létrehozandó program típusának kiválasztása. Ha létre akarsz hozni egy Expert Advisort, válaszd az Expert Advisor opciót és kattints a Nextre. A következő ablakba írd be a létrehozandó Expert Advisor nevét. Legyen ez a név a create.mq4 A létrehozandó fájl nevét a kiterjesztése nélkül írjuk. Az Expert Advisor Wizard egy többmezős ablakot fog nyitni, töltsük azt ki: 27. ábra. Egy Expert Advisor általános paramétereinek az ablaka

59 Az Ok-ra kattintva a főablakban egy szöveg fog megjelenni és a létrehozott Expert Advisor create.mq4 teljes neve meg fog jelenni a fájlrendszerben és a navigátorablakban. 28. ábra. A fájlrendszerben és a navigátorablakban is láthatjuk a létrehozott Expert Advisor fájlt. Lássuk a programszöveget, amit a Meta Editorlétrehozott : // // create.mq4 // John Smith // // #property copyright "John Smith" #property link "www.company.com" // // expert initialization function // int init() //---- //---- return(0); // // expert deinitialization function // int deinit() //---- //---- return(0); // // expert start function // int start() //

60 //---- return(0); // Láthatjuk, hogy ez a kód főleg megjegyzéseket tartalmaz. Már tudjuk, hogy a megjegyzések egy program nem kötelező részét alkotják és a megjegyzések szövegét nem hajtja végre a program. Van három különleges függvény a programban: init(), start() és deinit(). Mindegyik függvény csak egy operátort tartalmaz - return(0) ez az operátor kilépés a függvényből. Ez a programkód, amit az Expert Advisor Wizard létrehozott, csak egy minta, amely alapján a programozó létre tud hozni egy új programot. A végső programkód kötelezően nem kell, hogy tartalmazza mindegyik különleges függvényt. Ezek a mintában jelen vannak, mert egy szokásos, közepes szintű program tartalmazza ezek közül a függvények közül mindegyiket. Ha a függvények közül bármelyiket nem fogjuk használni, akkor azt törölni lehet. A programkódból a következő sorokat szintén kihagyhatjuk: #property copyright "John Smith" #property link "www.company.com" Bár ez a program gyakorlatilag nem használható, azt a szintaxis szempontjából helyesen írták. Ezt a programot lefordíthatnánk és elindíthatnánk. Az végrehajtódna, mint bármilyen másik program (bár számításokat nem végezne, mert ilyenek nincsenek a forráskódban). A program megjelenése A programokban erősen ajánlott a megjegyzéseket használni és néhány esetben ez nagyon fontos. Azt hangsúlyozni kell, hogy egy programozó nem csak programokat ír de olvassa is őket. Néha jelentős nehézséget okoz egy program olvasása. A tapasztalat azt mutatja, hogy sok programozó az érvelő logikák, ami alapján a programot fejlesztette, el szokta felejteni (vagy egy másik programozó írta a terméket) és megjegyzések nélkül nehéz, néha lehetetlen, megérteni a kódrészeket. Egy helyesen kódolt program minden esetben tartalmaz megjegyzéseket. A megjegyzések fő előnyei hogy: Először, a megjegyzések elválasztják a logikailag összefüggő programrészeket egymástól. Sokkal könnyebb olvasni egy helyesen formázott szöveget egy folyamatos szövegnél. Másodjára a megjegyzések lehetővé teszik, hogy a programozó egyszerű szavakban megmagyarázza, mit gondolt a kódsorok írásakor. Harmadszor egy program felső részében a programmal kapcsolatos általános információt részletezhetünk: a szerző nevét és kapcsolatatát (weboldal, , stb.), a program célját (egy teljes kereskedő program vagy egy könyvtári függvény), a fő jellemzőit és korlátozásait és egyéb hasznos információkat. Minden programozó kiválaszthatja magának a megjegyzések kényelmes stílusát. Azt a stílust, amit az MQL4 fejlesztők ajánlottak a create.mql4. Expert Advisor fájlon mutatjuk be. Nézzük meg a megfelelő megjelenési stílusok fő jellemzőit! 1. Egy megjegyzéssor-hossznak nem szabad túlnyúlni a főablakon. Ez a korlátozás nem a nyelvi szintaxis követelménye, de egy olyan programot olvasni, ami túlnyúlik a szegélyen, nem kényelmes. Bármilyen hosszú sort több sorba választhatunk, míg teljesen láthatók nem lesznek a képernyőn. Egy 1024 x 768 pixel felbontású monitoron a legnagyobb sorhossz 118 karakter. 2. Változók deklarációját célszerű a programkezdetnél elhelyezni. Ajánlott mindegyik változóhoz megjegyzést írni : röviden megmagyarázzni a jelentésüket és, ha szükséges, a használat sajátosságait. 3. Mindegyik operátort célszerű külön sorban elhelyezni

61 4. Ha megjegyzés van a sorban, azt a 76 pozícióból kell elindítani ( 17 monitor 1024 x 768 felbontással). Ez a követelmény nem kötelező. Például, ha egy kódsor 80 karakter, ez nem szükséges megosztani két sorba, a megjegyzést indíthatjuk a 81. karaktertől. Általában a program kódrésze 50 karakter hosszú sorokat tartalmaz és a megjegyzések, mint egy szövegoszlop a képernyő jobb szélén helyezkednek el. 5. A logikailag különálló részek elválasztására teljes szélességben kötőjelekből álló folytonos karaktersort használnak (118 karakter). 6. Amikor kapcsos zárójeleket használnak, egy tabulálás méretű behúzást kell használni (általában 3 karakter). Lássuk, hogyan néz ki egy Expert Advisor programkód. Ebben az esetben a programalgoritmus logikáját nem tárgyaljuk. Csak a program megjelenésével foglalkozunk. Egy program (Expert Advisor create.mq4) kinézete a következő lehet: // // create.mq4 // Az MQL 4 könyben használtpélda. // int Count=0; // Globális változó // int init() // Az init() különleges függvény Alert ("Az init() függvény elindult"); // Riasztás return; // Kilépés az init()-ből // int start() // A start() függvény double Price = Bid; // Helyi változó Count++; // Tick számláló Alert("New tick ",Count," Price = ",Price);// Riasztás return; // Kilépés a start() függvényből // int deinit() // A deinit() függvény Alert ("Funct. deinit() triggered at exit"); // Riasztás return; // kilépés a deinit()-ből // Látható, hogy a programot több blokkra osztották és megjegyzésekkel (folyamatos sorokkal) tagolták. Ez egy módszer arra, hogy elválasszuk a különleges függvényeket és a program fejrészét: // A változókat egy olyan különálló blokkban deklarálták, ahol mindegyik változót leírtak. Néha a programok olyan változókat tartalmaznak, melyek magyarázatához többen sort kell használni. Ez ritka eset, de ha ez történik, ezt a megjegyzést mindenféleképpen el kell helyezni, különben nem csak egy másik programozó, de maga a szerző sem lesz képes arra, hogy egy idő után megértse ezt a részt. Mindegyik kódsor jobb oldala tartalmaz egy magyarázó megjegyzést. A megjegyzések fontosságát érzékeltethetjük, ha egy program nem tartalmaz ilyeneket akkor problémát okoz az algoritmus megértése a program olvasás közben. Például, ha ugyanazt a kódot mutatjuk be megjegyzések és blokkelválasztás nélkül, nehezebb lesz olvasni azt, bár a program eléggé egyszerű és rövid: int Count=0; int init() Alert (Funct. init() triggered at start"); return; int start() double Price = Bid; Count++; Alert("New tick ",Count," Price = ",Price); return;

62 int deinit() Alert (""Funct. deinit() triggered at exit"); return; A program lefordítása Hogy gyakorlatban használhatóvá tegyünk egy programot, azt le kell fordítani. Erre a gombot vagy az F5-öt kell használni a Meta Editorban. Ha a program nem tartalmaz semmilyen hibát, a fordítás megtörténik és egy üzenet fog a szerszámosládában megjelenni: 29. ábra. Szerkesztőüzenet egy sikeres programfordításról. Ezután az új fájl, a create.ex4 me gfog jelenni a megfelelő mappában (ebben az esetben Terminal_directory\experts). Ez egy olyan program, ami készen áll a végrehajtásra a MetaTrader4 ügyfélterminálban. A fordítása alatt ugyanazon néven a program forrásszövegének az utolsó verziója (ez esetben ez a fájl a create.mq4) ugyanabba a mappába lesz elmentve. Ugyanakkor a létrehozott Expert Advisor neve meg fog jelenni az ügyfélterminál navigátor-ablakának az Expert Advisors szekciójában: 30. ábra. Láthatjuk az ügyfélterminál navigátor-ablakában levő Expert Advisorok nevét. Ha a fordítás alatt hibát talál a programban, a Meta Editor a megfelelő hibaüzenetet fogja mutatni. Ezesetben vissza kell térni a forrásszöveghez, ki kell javítani a hibákat és meg kell kísérelni még egyszer lefordítani a programot. A sikeres fordítás csak akkor lehetséges, ha a programban nincsenek hibák. Egy program gyakorlati használata Ha egy alkalmazási program (Expert Advisor, script vagy indikátor) sikeresen le lett fordítva és a neve megjelent az ügyfélterminál navigátor-ablakában, azt a gyakorlatban használhatjuk. Készen áll arra, hogy az ikont az egérrel áthúzzuk navigátorablakból a szimbólumablakba ('drag & drop' módszer). Ezzel a művelettel a programot hozzá csatoltuk egy szimbólumablakhoz és a végrehajtása indult. Egy Expert Advisor és egy indikátor addig fog működni, amíg a felhasználó kézzel leállítja a programvégrehajtást. Egy szokásos script abba fogja hagyni a működését miután végrehajtotta az algoritmusát. Még egyszer felhívjuk a figyelmedet:

63 Bármilyen alkalmazási programot (Expert Advisor, indikátor, script) csak arra tudod használni, hogy a MetaTrader 4 Client Terminal részeként kereskedjen, amikor ez az Interneten keresztül a szerverhez kapcsolódik (a dealing centerhez). A programok közül egyik sem települ a szerverre és nem használhatók más fejlesztésű ügyfélterminálokban. Más szóval, ha a kereskedő használni akar valamilyen alkalmazási programot, neki fel kell kapcsolnia a számítógépét, el kell indítania a MetaTrader 4 Client Terminalt és el kell indítania az *.ex4 állományt egy szimbólum ablakban. A program működése alatt (az algoritmusától függően) megbízásokat küld a szerverre miközben a kereskedelmi menedzsmentet vezeti

64 Programozás MQL4-ben Előre kell bocsájtani, hogy az MQL4 programozás mindenki számára elérhető, de ez figyelmet és biztos tudást igényel. Lehet, hogy néhány kereskedő arra számít, hogy a programozás tanulása azt jelenti, hogy nekik azokat a bonyolult folyamatokat kell megismerniük, amelyek a számítógépen belül lezajlanak. Szerencsére az MQL4 nyelv fejlesztői megpróbálták azt a felhasználók széles körének elérhetővé tenni. Az MQL4 programok létrehozásának egy előnyös sajátsága az, hogy a programozónak nem szükséges az ügyfélterminálnak az operációs rendszerrel való kölcsönhatására vonatkozó különleges tudással rendelkeznie, nem kell ismernie a hálózati protokollt vagy a fordító szerkezetét. Az a folyamat amikor MQL4 programokat hozzunk létre, egy egyszerű és barátságos munka. Például a vezetőnek nem kell ismernie a motor szerkezetét ahhoz, hogy autót vezessen, - neki csak a pedál és a kormány kezelését kell megtanulnia. Mindazonáltal, mindegyik vezető keresztülment rázós utakon a vezetés tanulása során. Amit tennie kellene egy kezdődő programozónak - megtanulni a programok létrehozásának néhány egyszerű elvét és azután lassan elindulni a programozással. Programszerkezet Bár több fajta MQL4 program van, ezeknek vannak közös jellemzőik. Azt mondhatjuk, hogy a helyes programszerkezet a helyesen írott kód alapja. Ezért kell a program összetevőit megismerni. Különleges függvények Sok beépített függvény van az MQL4 nyelvben. Az ilyen függvényeket a nyelv általános függvényeinek nevezik. De van néhány rendkívül fontos függvény, amit különleges függvényeknek neveznek. Egy programot nem hajthatunk végre nélkülük. Ezek közül a függvények közül mindegyiknek meghatározott rendeltetése van. Programvégrehajtás Az embernek meg kell értenie, hogy egy MQL4-program hogyan működik. Nem minden kódrészt használnak ugyanolyan gyakorisággal. Milyen függvényeket hajtunk végre legelőször, hol kell egy program fő részét elhelyezni, milyen programtípust kell erre vagy arra a célra használni? Példák a megvalósításra Egy új nyelvet a legjobb példákon keresztül megtanulni. Hogyan írjunk helyesen egy egyszerű programot? Milyen hibák történhetnek?

65 Programszerkezet Az előző részekben megtanultuk az MQL4 programnyelv néhány alapvető fogalmát. Most azt tanulmányozzuk, hogy egy programnak általában milyen a szerkezete. Ezen probléma megoldásához most meg fogjuk vizsgálni a programok szerkezeti elrendezését. Ahogy már fent említettük, a főprogramkódot, amit a programozó írt, a felhasználói és a különleges függvényekbe helyezzük. A Függvények részben megvitattuk a beépített és felhasználói függvények fogalmát és tulajdonságait. Röviden: egy felhasználói függvénynek van leírása, a függvényhívást arra használjuk, hogy elindítsa a függvény végrehajtását a programban. Bármilyen beépített vagy felhasználói függvényt csak akkor hajthatunk végre, miután azt meghívtuk; ebben az esetben azt mondjuk, hogy a függvényt egy program végrehajtásáért hívta. A különleges függvények tulajdonságait részletesen a Különleges függvények részben írjuk le. Itt csak a velük kapcsolatos fontosabb ismereteket fogjuk megtanulni. A különleges függvény egy olyan függvény, amit az ügyfélterminál hív, hogy azt végrehajtsa. A beépített függvényektől eltérően a különleges függvényeknek van függvényleírásuk és a különleges függvények nem hívhatók a programból. A különleges függvényeket végrehajtásra csak az ügyfélterminál hívhatja, (technikailag van arra lehetőség, hogy egy különleges függvényt a programból hívjunk, de ez a módszer helytelen és itt nem fogjuk azt megtárgyalni). Amikor egy programot elindítanak egy szimbólum ablakban, az ügyfélterminál a vezérlést átadja a különleges függvények közül az egyiknek. Végül ezt a függvényt végrehajtja. Az MQL4 programozási szabálya a következő: A programkódot függvényekbe kell írni. Azok az eszközök, programsorok (operátorok és függvényhívások) amelyek a függvényeken kívül vannak nem hajthatók végre. Ha egy ilyen programot akarunk lefordítani, a MetaEditor hibaüzenetet ad és a végrehajtható *.ex4 állományt nem fogja létrehozni. Vizsgáljuk meg egy egyszerű program funkcionális felépítését - Expert Advisor: 31. ábra. Egy program szerkezet terve (Expert Advisor)

66 Egy MQL4program legfontosabb blokkjai: 1. A program fejrésze. 2. Különleges függvény init(). 3. Különleges függvény start(). 4. Különleges függvény deinit(). 5. Felhasználói függvények. Továbbiakban csak ezeknek a funkcionális blokkoknak a belső tartalmát fogjuk elemezni (egy program integrált részeit), és minden külső objektum (például, az ügyfélterminál információs felülete vagy a hardver) most számunkra érdektelen. A MetaTrader 4 információs környezete Az MT4ügyfélterminál információs környezete nem a program alkotórésze. Az információs környezet a rendelkezésre álló paraméterek készlete, amely paramétereket a program felhasználhat. Például egy olyan ár-adat, ami egy új tickkel jött, a kereskedelem volumene, információ az előzményekről, a legnagyobb és legkisebb árakról, azok a paraméterek, amik kereskedési feltételeket jellemezik. Az információs környezet adatait frissíti az ügyfélterminál, amikor kapcsolatban állt a szerverrel és egy új tick érkezik. Programszerkezet Fejrész A fejrész a program kezdetén lévő sorokból áll (az első sorral kezdődik) és néhány leírást tartalmaz. Ezek a sorok a programmal kapcsolatos általános információkat tartalmaznak. Például ez a rész tartalmazza a globális változók deklarációját és inicializációját (azt, hogy milyen információt szükséges tartalmaznia a fejrésznek, később fogjuk megvitatni). A fejrész a legelső függvényleírásig tart (felhasználói vagy különleges függvény). Különleges függvények Általában a program fejrésze után a különleges függvényeket írjuk le. A különleges függvényleírások hasonlítanak egy szokásos felhasználói függvény leíráshoz, de a különleges függvényeknek előre definiált neveik vannak: init(), start() és deinit(). A különleges függvényekben a számítások egyes blokkjai az ügyfélterminál nformációs környezetével valaminti a felhasználói függvényekkel is kapcsolatokban vannak. A különleges függvényeket részletesen bemutatjuk a Különleges függvények részben. Felhasználói függvények A felhasználói függvények leírását általában a különleges függvények leírása után helyezük el. Egy programban levő felhasználói függvények száma nem korlátozott. Ez a séma csak két felhasználói függvényt tartalmaz, de egy program tartalmazhat 10-et vagy 500-at, vagy egyet sem. Ha felhasználói függvényeket nem használnak egy programban, a program szerkezete egyszerűbb lesz: fejrész és a különleges függvények leírás

67 Standard (beépített) függvények Ahogy korábban említettük, a beépített függvényeknek csak a függvényhívását tüntetjük föl. A beépített függvényeknek, különleges és a felhasználói függvényekkel ellentétben nem adunk függvényleírás. A standard (beépített) függvények leírása el van rejtve, nem látja a programozó és azért nem tud változtatni rajta. Ezeket csak a MetaEditor érheti el. A program fordítása alatt MetaEditor létre fog hozni egy végrehajtható állományt, amiben minden hívott beépített függvényt teljes terjedelmében be fog illeszteni. A programrészek elrendezése A fejrésznek az első sorokban kell elhelyezkednie. A különleges és felhasználói függvényleírások elrendezése nem számít. 32. ábra mutatja a funkcionális blokkok egy szokásos elrendezését: fejrész, különleges függvények, felhasználói függvények. A 33. ábra más programszerkezeti változatokat mutat. Minden példában a fejrész jön először, míg a függvényeket véletlen sorrendben írhatjuk le. 32. ábra. Egy programban levő funkcionális blokkok szokásos elrendezése (ajánlott). Jegyezzük meg: 33. ábra. A funkcionális blokkok lehetséges szervezése egy programban (véletlen sorrend). A függvények közül semelyik nem írható le egy másik függvényben. Tilos a programban olyan függvényleírások használata, amiket egy másik függvényben helyeztek el. Lent példák a függvényleírások helytelen elrendezésére

68 34. ábra. Példák egy programban levő függvény helytelen elrendezésére. Ha egy programozó tévedésből egy olyan programot hoz létre, ahol a függvények közül bármelyiknek a leírása egy másik függvényen belül van, a fordítás során a MetaEditor egy hibaüzenetet fog mutatni, és a végrehajtható állományt nem fogja létrehozni. Kódvégrehajtási sorrend Fejrész és különleges függvények A programvégrehajtás kezdetén a programfejrészben lévő sorok végrehajtódnak. Az előkészületek után, amiket a fejrészben leírtak, az ügyfélterminál átadja a vezérlést az init() különleges függvénybe és a függvényt végrehajtja (a vezérlésátadást a szerkezeti tervnél nagy sárga nyilak mutatják). Az init() különleges függvényt a programművelet kezdetén csak egyszer hívják és hajtják végre. Általában ez a függvény olyan kódot tartalmaz, amit csak egyszer kell végrehajtani, mielőtt a program fő működése elkezdődik. Például, amikor az init() függvényt végrehajtják, néhány globális változót inicializálnak, grafikus objektumokat rajzolnak, üzeneteket mutathatnak. Az init() függvényben lévő programsorok végrehajtása után ez a függvény befejezi a futását és az irányítás visszatér az ügyfélterminálhoz. A leghosszabb végrehajtási idő a start() függvény végrehajtásának az időszakára esik. Bizonyos feltételek esetén (a különleges függvények jellemzőit lásd a Különleges függvényekrészben), egy új ticknek az ügyfélterminálnak a szerverről történt fogadása után, az ügyfélterminál végrehajtásra hívja a start() különleges függvényt. Ez a függvény (mint a többi függvény) az ügyfélterminál információs környezetéhez tud fordulni, számításokat tud elvégezni, megbízásokat tud bezárni és nyitni, vagyis végrehajthat bármilyen műveletet, amit az MQL4megenged. Amikor a start() különleges függvényt végrehajtják vezérlőutasítás is keletkezhet (piros nyíl). Ezek lehetnek kereskedelmi utasítások a megbízások nyitására bezárására vagy módosítsára. Miután az EA start() különleges függvényének a kódja teljesen végrehajtódik, a start() függvény befejezi a működését és az irányítást visszaküldi az ügyfélterminálnak. A terminál magánál fogja tartani az irányítást és nem indít el semmilyen különleges függvényt. Egy szünet keletkezik, ami alatt a program nem működik. Később, amikor egy új tick jön az ügyfélterminál a vezérlést ismét át fogja adni a start() különleges függvénynek, a függvény még egyszer végrehajtódik és miután a végrehajtása kész, az irányítást vissza fogja küldeni az ügyfélterminálnak. A következő ticknél a start() függvényt ismét el fogja indítani az ügyfélterminál. A start() különleges függvénynek ügyfélterminál általi hívása addig ismétlődik, amíg a program hozzá van kapcsoljva egy szimbólumábrához, ez akár heteken, hónapokon át folytatódhat. Ezen időszak alatt az Expert Advisor automatikusan kereskedik, vagyis ellátja fő feladatát. A folyamatábrán a start() függvénynek a többszörös végrehajtását a sárga nyilak jelzik, amelyek körbeveszik a start() különleges függvényt. Amikor a kereskedő leválaszt egy Expert Advisort egy chartról, az ügyfélterminál egyszer elindítja a deinit() különleges függvényt. Ennek a függvénynek a végrehajtása nélkülözhetetlen egy EA helyes bezárásához. Működése alatt a program például létrehozhat grafikus tárgyakat és az ügyfélterminál globális változóit. Ezért a deinit() függvény kódja olyan programsorokat tartalmaz, amelynek végrehajtása a fölösleges

69 objektumok és változók törlésével végződik. A deinit() különleges függvény végrehajtása után, a vezérlés visszatér az ügyfélterminálhoz. A végrehajtott különleges függvények fordulhatnak az információkörnyezethez (a vékony kék nyilak a sémán) és hívhatnak felhasználói függvényeket (a vékony sárga nyilak). Jegyezzük meg: a különleges függvényeket végrehajtásra az ügyfélterminál hívja, a sorrendjük előre definiált: először az init(), azután a start() ismétlődő hívása és végül a deinit(). Azokat a feltételeket amelyek esetén az ügyfélterminál különleges függvényeket hív a Különleges függvények részben írjuk le. Felhasználói függvények Felhasználói függvényeket akkor hajtanak végre mikor valamely függvény egy felhasználói függvényhívást tartalmaz. Ebben az esetben vezérlés időlegesen átkerül a felhasználói függvénybe és miután a függvény végrehajtásának vége van, a vezérlés visszatér a hívás helyére (a vékony narancs nyíl a sémán). A felhasználói függvények hívása nem csak egy különleges függvény leírásából lehetséges, hanem egy másik felhasználói függvény leírásából is. A felhasználói függvények másik felhasználói függvényből történő hívása a programozásban megengedhető és széles körben használt módszer. Felhasználói függvények végrehajtása nem hívható az ügyfélterminálból. Bármilyen felhasználói függvényt végrehajtható egy különleges függvény végrehajtása során és vezérlő utasításokat is küldhetnek vissza az ügyfélterminálra. Felhasználói függvények szintén használhatják az ügyfélterminál információs környezet változóinak értékeit (a vékony kék nyíl a sémán). Ha egy program tartalmazza egy felhasználói függvény leírását, de ennek a függvénynek hívása nincs, ezt a felhasználói függvényt ki fogják hagyni a kész programból a fordításnál és nem fogja használni azt a program a működése során. Jegyezzük meg: a különleges függvényeket az ügyfélterminál hívja végrehajtásra. A felhasználói függvények végrehajtódnak, ha különleges függvényekből vagy egy másik felhasználói függvényből hívják, de soha nem hívja őket az ügyfélterminál. Kereskedelmi megbízást a különleges és a felhasználói függvény egyaránt tud adni

70 Különleges (speciális) függvények A MetaTrader 4 ügyfélterminálban működő programoknak az az egyik fő sajátossága, hogy állandóan frissített információkkal, valós idejű módban dolgoznak. Az MQL4 nyelv ezen sajátsága visszatükröződik a három különleges függvény alakjában: init(), start() és deinit(). A különleges függvények különleges tulajdonságokkal felruházott, előre definiált nevekkel - init(), start() és deinit() - rendelkező függvények. A különleges függvények tulajdonságai A különleges függvények közös tulajdonsága Minden különleges függvény közös tulajdonsága az, hogy a programból történő függvényhívás nélkül, bizonyos feltételek megléte esetén végrehajtódnak a programban. A különleges függvényeket végrehajtásra az ügyfélterminál hívja. Ha egy program tartalmazza egy különleges függvény leírását, azt hívni fogják (és végrehajtódik) a hívó feltételekkel összhangban (saját tulajdonságok). A különleges függvényeket végrehajtásra híják, csak az ügyfélterminál hívhatja őket. A különleges függvények egyéni tulajdonságai Az init() különleges függvény. Az init() különleges függvény tulajdonsága, hogy a program inicializálásnál azt végrehajtják. Ha egy program tartalmazza az init() különleges függvény leírását, azt hívni fogják (és végrehajtják) a program indításánál. Ha init() különleges függvény nincs egy programban, a programkezdésnél nem történik végrehajtás. Az Expert Advisorban az init() különleges függvényt hívják (és végrehajtják) az ügyfélterminál indításánál a történelmi adatok betöltése után, a Meta Editorban levő program újrafordítása után, chart ablak és idősík váltás után, azután, hogy az EA tulajdonságok ablakában az input paramétereket megváltoztatjuk, és az után, hogy számlaegyenleg megváltozik. A scriptben az init() különleges függvényt hívják (és végrehajtják) közvetlenül az elindítása után. Egyéni indikátorokban az init() különleges függvényt, hívják (és végrehajtják) közvetlenül az ügyfélterminál indítása után, chart ablak és idősík váltás után, a MetaEditorban levő program újrafordítása után és az egyéni indikátor tulajdonságok ablakban lévő input paraméterek változása után. A start() különleges függvény. A start() különleges függvény működése a végrehajtható program típusától függ

71 Az Expert Advisorban a start() különleges függvényt, hívják (és végrehajtják) amint egy új tick jön. Ha az új tick a start() különleges függvény végrehajtása alatt jött, ezt a ticket figyelmen kívül fogják hagyni, vagyis ilyen esetben a start() különleges függvényt nem fogják hívni. Minden fogadott árváltozást a start() különleges függvény végrehajtása alatt figyelmen kívül fognak hagyni. A start() különleges függvény végrehajtását csak akkor kezdeményezi az ügyfélterminál, ha az előző munkamenet befejeződött, a vezérlés visszakerült az ügyfélterminálra és a start() különleges függvény egy új tickre vár. Lehetséges a start()különleges függvény végrehajtását befolyásolni az "Enable/disable Expert Advisors"gombbal. Ha ez a gomb az ügyfélterminálban ki van kapcsolva, tiltja az EA működését, és az ügyfélterminál nem fogja hívni a start() különleges függvényt függetlenül attól, hogy az új tickek jönnek-e, vagy sem. Azonban a gomb kikapcsolása vagy bekapcsolása nincs befolyással a program aktuális végrehajtására (a bekapcsolásakor nem fut le a start() függvény). A start() különleges függvényt, nem hívja az ügyfélterminál, ha az EA tulajdonságok ablaka nyitva van. Az EA tulajdonságok ablakát csak akkor tudjuk kinyitni mikor a start() különleges függvény egy új tickre vár. Ezt az ablakot nem nyithatjuk ki a start() különleges függvény végrehajtása alatt. A scriptben a start() különleges függvényt egyszer hívják (és hajtják végre) közvetlenül a program inicializálást elvégző init() különleges függvény lefutása után. Egyéni indikátorokban a start() különleges függvényt hívják (és végrehajtják) azonnal amint az indiktort hozzácsatolják egy charthoz, egy új tick érkezésekor, az ablakméret megváltoztatásakor, amikor átkapcsolunk egyik ablakról egy másikra, mikor az ügyfélterminált elindítjuk (ha korábban az indikátort csatoltuk egy charthoz), ha a kereskedett szimbólumot (instrumentum) vagy az idősíkot megváltoztatjuk, függetlenül a tickek érkezésétől. Minden programtípusban a start() függvényt végrehajtják, amikor egy programot eltávolítanak egy chartról, amikor ablakot és/vagy idősíkot változtatnak, amikor egy számla megváltozik, amikor egy ablak bezáródik, vagy ha az ügyfélterminál befejezi a működését. Ha a start() különleges függvény az ügyfélterminál leállításakor fut, a terminálnak 2.5 másodperc áll rendelkezésére, hogy befejezze a függvény végrehajtását,. Ha a leállítási parancs után a start() különleges függvény a jelezett határidőnél tovább folytatja a működését, azt erőszakosan be fogja fejezni az ügyfélterminál. A deinit() különleges függvény A deinit() különleges függvény feladata, hogy a program befejezésekor lefusson (deinicializáció). Ha egy program tartalmazza a deinit() különleges függvény leírását, azt hívni fogják (és végrehajtják) a programleállításnál. Ha egy program nem tartalmazza a deinit() függvényt akkor nem kell műveleteket végrehajtani a programleállításnál. A deinit() különleges függvényt hívja az ügyfélterminál a terminál leállításnál, amikor egy chart ablakot bezárunk, mikor ablakot és/vagy idősíkot váltunk, a Meta Editorban történt sikeres program újrafordításnál, mikor megváltoznak az inputparaméterek, és mikor egy számlán változás történik. Expert Advisorokban és scriptekben programleállítás és a deinit() különleges függvény hívása történik amikor egy chart ablakhoz csatolt új program felváltja a előző ugyanazon típusú programot. Egyéni indikátorokban a deinit() különleges függvényt nem hajtják végre, amikor egy új indikátort hozzácsatolnak egy charthoz. Több indikátor is tud működni egy ablakban, vagyis egy új indikátor csatolása az ábrához nem követeli meg a működők leállítását és a deinit() függvény hívását. A deinit() végrehajtási ideje 2.5 másodpercre van korlátozva. Ha a deinit() különleges függvény kódja ennél tovább futna, az ügyfélterminál erőszakosan be fogja fejezni a deinit() különleges függvény végrehajtását és a program működését

72 A különleges függvényekkel szembeni követelmények Az init() és deinit() különleges függvények hiányozhatnak egy programból. A különleges függvények leírásának a sorrendje egy programban közömbös. A különleges függvények a függvényhívások általános szabályai szerint hívhatjók, bárhol is helyezkednek el a programba. A különleges függvényeknek lehetnek paramétereik. Mindazonáltal, amikor ezeket a függvényeket hívja az ügyfélterminál, paramétereket nem fog küldeni kívülről, az alapértelmezett értékeket fogja használni. Az init() és deinit() különleges függvények működését még azelőtt be kell fejezni, mielőtt a start() függvény hívása megtörténik. A különleges függvények használatának szabályai A fejlesztők a programozóknak egy nagyon kényelmes eszközt adtak: amikor egy program végrehajtása elkezdődik, az init() függvény fut le, azután a fő munkát a start() függvény segítségével hajtják végre, és mikor egy folyamat befejeződik a deinit() függvényt a programleállítást megelőzően el fogják indítani. Egy program fő kódját a start() függvénynek kell tartalmaznia. Minden operátort, beépített és felhasználói függvényhívást és minden szükséges számítást ebben a függvényben kell végrehajtani. Ugyanakkor az embernek helyesen kell értelmezni ennek a függvénynek a szokásos szerepét. A felhasználói függvények leírása a különleges függvények leírásán kívül helyezkedik el a programkódban, de ha egy felhasználói függvényt végrehajtásra hívnak a különleges függvény nem fejezi be a működését. Ez azt jelenti, hogy a vezérlést megkapja a felhasználói függvény, de maga a felhasználói függvény azon a különleges függvényen belül működik, amelyik hívta azt. Tehát a programvégrehajtás során a különleges függvény mindig működik (a saját tulajdonságai szerint) és a felhasználói függvények akkor hajtódnak végre mikor a különleges függvények hívják őket. Ha egy programozó nem akarja használni valamely különleges függvényt, ezt megteheti. Ez esetben az ügyfélterminál nem fogja hívni azt. A teljesen szabályos program tartalmazza a három különleges függvény mindegyikét. Ha egy programban nincs init() vagy deinit() vagy egyik sem az szintén elfogadható. Ha a különleges függvények közül semelyiket nem használják egy programban, ezt a programot nem fogják végrehajtani. Ügyfélterminál végrehajtásra csak a különleges függvényeket hívhatja. Egyedi függvényeket nem hív az ügyfélterminál. Ezért ha egy program egyáltalán nem tartalmaz különleges függvényeket (és csak egyedi függvényeket tartalmaz), azt végrehajtásra soha nem fogják hívni. Nem ajánlott az init() függvényből hívni a a start() függvényt vagy kereskedelmi függvényeket végrehajtani az init() függvényből, mert az információkörnyezeti paraméterek értékei lehet, hogy nem állnak kész (a charttal kapcsolatos információk, piaci árak, stb.). A Programvégrehajtás és a Példák a megvalósításra részek gyakorlati példákat tartalmaznak és segíteni fognak a különleges függvények további tulajdonságainak megismerésében

73 Programvégrehajtás A programozási képesség gyorsabban fejleszthető, ha a programozónak már van egy kicsi működő programja. Hogy megértse az egész programot, alaposan meg kell vizsgálnia az összetevői és lépésről lépésre nyomon kell követnie a műveleteket. Jegyezzük meg: a különböző alkalmazási programok különleges függvényeinek tulajdonságai (Expert Advisorok, scriptek, indikátorok) különbözőek. Most elemezni fogjuk, hogy egy Expert Advisor hogyan működik. Példa egy egyszerű Expert Advisorra: (simple.mq4) // // simple.mq4 // To be used as an example in MQL4 book. // int Count=0; // Global variable // int init() // Spec. funct. init() Alert ("Function init() triggered at start");// Alert return; // Exit init() // int start() // Spec. funct. start() double Price = Bid; // Local variable Count++; // Tick counter Alert("New tick ",Count," Price = ",Price);// Alert return; // Exit start() // int deinit() // Spec. funct. deinit() Alert ("Function deinit() triggered at deinitialization"); // Alert return; // Exit deinit() // A programvégrehajtás szabályaival összhangban (lásd: Programszerkezet és Különleges függvények) ez az Expert Advisor következő utat fogja követni: 1. Amikor egy programot csatolnak egy charthoz, az ügyfélterminál a vezérlést átadja a programnak és a program végrehajtása elindul. A programvégrehajtás a fejrésszel indul. A fejrész csak egy sort tartalmaz: int Count=0; // Global variable Ebben a sorban a Count globális változót deklaráljuk és inicializáljuk, értéke nulla. (A helyi és globális változókat elemezni fogjuk A változók típusai részben). Ennek a változónak a deklarációját itt kell elhelyezni, mert ez az algoritmus, amit ebben a bizonyos programban használunk, igényli a Count változót. Azért kell globális változónak lennie és azért nem deklarálhatjuk egy függvényen belül, hogy a Count változó, globális változóként bármelyik programrészből (bármelyik függvényből) elérhető legyen. 2. A fejrész végrehajtása után az init() különleges függvény végrehajtása kezdődik. Jegyezzük meg, ennek a függvénynek a hívását nem tartalmazza a programkód. Az init() végrehajtása akkor kezdődik, amikor az EA-t hozzácsatolják egy charthoz. Az ügyfélterminál hívni fogja és végrehajtja az init() függvényt, mert a programkód tartalmazza az init() függvényleírását. Az elemzett programban az init() különleges függvény leírása a következő: int init() // Spec. funct. init() Alert ("Function init() triggered at start");// Alert

74 return; // Exit init() A függvénytörzs csak két operátort tartalmaz. 2.1 Az Alert() függvény egy riasztási ablakot nyit: Function init() triggered at start 2.2 A return operátor befejezi az init() különleges függvény működését. Az init() függvény végrehajtásnak eredményeképpen egy riasztási ablak nyílik. Valójában egy ilyen algoritmus használata a programokban nagyon ritka, mert az init() ilyen használata kevés információt nyújt. Nincs értelme annak, hogy egy függvény tájékoztassa a kereskedőt arról, hogy őt végrehajtották. Itt az algoritmust csak az init() végrehajtásának megjelenítésére használjuk. Figyelem: az init() különleges függvényt csak egyszer hajtják végre egy programban. A függvény végrehajtás a programoperáció kezdetén zajlik, a fejrész feldolgozása után. Amikor a return operátort végrehajtják az init() különleges függvényben az a programvezérlést visszaküldi az ügyfélterminálnak. 3. Az ügyfélterminál észlelte a start() különleges függvény leírását a programban: int start() // Special funct. start() double Price = Bid; // Local variable Count++; Alert("New tick ",Count," Price = ",Price);// Alert return; // Exit start() 31. A vezérlés az ügyfélterminálnál van. Az ügyfélterminál egy új tickre vár és nem indítja el programfüggvényeket addig amíg egy tick nem jön. Ezen idő alatt a program nem működik, semmilyen műveletet nem hajt végre. Egy szünet keletkezik, bár semmilyen közvetlen vagy közvetett utasítás sincs a szünet tartására. Az új tickre való várakozás a start() függvény egyedi tulajdonsága és programozással nem lehet ezen a tulajdonságon változtatni (például kikapcsolni). A program várni fog a vezérlésre addig amíg egy új tick jön. Amikor egy új tick jön, az ügyfélterminál átadja az vezérlést programnak, mégpedig a start() különleges függvénynek. Végül a start() függvény végrehajtása elkezdődik. 32 (1). Az alábbi sorban double Price = Bid; // Local variable a következő műveleteket végezzük: 32.1(1). A Price helyi változó deklarálása (lásd: A változók típusai) Ennek a helyi változónak az értéke elérhető lesz a start() különleges függvény bármelyik részéről.(de csak onnan!). 32.2(1). Az értékadó operátor végrehajtása. Az aktuális ár értékét ki fogják jelölni a Price változónak. Az új ár értéke minden alkalommal megjelenik, amikor egy új tick jön (például az első ticknél az ár egyenlő lehet gyel). 33(1). Ezután a következő sort is végrehajtják: Count++; Ez a szokásos Count= Count+1 rekord analógja; Abban a pillanatban, amikor a vezérlés ehhez a sorhoz ér a Count változó érték egyenlő nullával. A Count++ végrehajtásának eredményeképpen, a Count értéke eggyel növekedni fog. Amikor a vezérlés a következő sorra lép a Count érték egyenlő lesz 1-gyel. 34(1). A következő sor Alert() függvényhívást tartalmaz: Alert ("New tick ",Count," Price = ",Price);// Alert A függvény ki fog írni minden állandót és változót, amit a zárójelben felsoroltunk

75 A start() függvény első végrehajtásakor először a New tick szöveget írja ki, majd Count változó értékét (első végrehajtásnál ez az érték 1), ezután a Price= szöveget és végül a Price változó értékét (a példánkban ez ). Végül a következő sort láthatjuk: New tick 1 Price = (1). operátor return; // Exit start() befejezi a start() különleges függvény munkáját. 36. A vezérlés vissza tér az ügyfélterminálhoz (amíg egy új tick nem jön). Az Expert Advisor végrehajtása a start() függvényben történik. Amikor a végrehajtásnak vége van, a start() különleges függvény a vezérlést visszaküldi az ügyfélterminálnak, és amikor egy új tick jön, az ügyfélterminál újból elindítja a függvényt. Ez a folyamat (a start() függvény végrehajtása és a vezérlés visszaadása az ügyfélterminálnak) sokáig folytatódhat akár napokig vagy hetekig. Ez alatt az idő alatt a start() különleges függvény újra és újra végrehajtódik. A környezeti paraméterektől függően (új árak, kereskedelmi feltételek, az idő, stb.) a start() különleges függvény különböző műveleteket, megbízások nyitását, zárását vagy módosítását hajthat végre. 37. Egy új tick érkezésekor, a pontok ismétlődnek. Azonban csak az operátorok végrehajtása ismétlődik, a változók minden alkalommal új értékeket kapnak. Nézzünk meg a start() függvény első és második végrehajtása közti különbségeket. 32 (2). Ebben a sorban: double Price = Bid; // Local variable a következő események történnek: 32.1(2). A Price helyi változó deklarációja (változatlan). 32.2(2). Az értékadó operátor végrehajtása. Az aktuális ár értékét fogja kapni a változó (az ár értéke minden alkalommal frissül amikor egy új tick érkezik, például a második ticknél az ár ) (változik). 33(2). A következő sor végrehajtása: Count++; Ezt sort a Count változó megelőző értéke határozza meg (a start() függvény első végrehajtása után a Count értéke egyenlő 1-gyel, Count++ ismételt végrehajtása eredményeképpen, értékét ismét növelni fogjuk eggyel. Így a második végrehajtásnál Count egyenlő lesz 2-vel (változott). 34(2). Alert() függvény: Alert ("New tick",count," Price = ",Price);// Alert minden állandót és változót (azok új értékeik) felsorol a zárójelben. A start() második végrehajtásnál a program a New tick szöveget irja ki, majd a Count változó értékét (második végrehajtásban ez egyenlő 2-vel) ezután a Price = szöveget és végül a Price változó értékét (a példánkban ) (változott). Végül a következő sort láthatjuk a riasztási ablakban: New tick 2 Price = (2). A következő operátor: return; // Exit из start() befejezi a start() működését (nincs változás). 36(2). A vezérlést visszaküldi az ügyfélterminálnak, hogy várjon egy új tickre

76 37(2)., Azután ez megint ismétlődik. A start() harmadik végrehajtásánál a változók ismét új értékeket fognak kapni és az Alert() függvény ismét ki fogja írni azokat, megismétlődik a 32-36(3) pont. Azután újra és újra: (4), (5),..(6)..(7)..(8)... ha a felhasználó nem avatkozik be, ez a folyamat vég nélkül ismétlődik. Ebben a programban a start() működésének eredményeképpen látni fogjuk az ár történelmi változását. A következő események csak akkor fognak megtörténni, amikor egy felhasználó úgy dönt, hogy befejezi a programot és erőszakosan kézzel lekapcsolja a programot egy chartról. 4. Ügyfélterminál a vezérlést átadja a deinit() különleges függvénynek (a tulajdonságaival összhangban). int deinit() // Special funct. deinit() Alert ("Function deinit() triggered at exit"); // Alert return; // Exit deinit() Csak két operátor van a függvény törzsben. 41. Az Alert() ki fogja írni: Function deinit() triggered at deinitialization 42. A return operátor befejezi a deinit() működését. A deinit() függvényt csak egyszer indítja el az ügyfélterminál, azután a szöveg az Alert() ablakban meg fog jelenni és a programot el fogja távolítani a chartról. 5. Itt az Expert Advisor végrehajtásának a története véget ér. Erősítsd hozzá ezt a mintaprogramot bármilyen charthoz és indítsd azt el. A működő program mutatni fog egy Alert() ablakot, ami tartalmaz minden eseményt, amit létrehozott a függvény. A figyelmeztető jelzéseket nem nehéz megérteni, és azt sem, hogy ezek a figyelmeztetések melyik különleges függvénytől származnak. 35. ábra. A program működésének az eredménye simple.mq4. Ebből a példából láthatod, hogy egy programot a különleges függvények azon tulajdonságaival összhangban hajtanak végre, amiket leírtunk a Különleges függvények bekezdésben. Zárd be a programot és indítsd el azt megint. Miközben ezt többször megteszed, tapasztalatot fogsz szerezni az első programoddal kapcsolatban. Ez működni fog most és a legközelebbi alkalommal is. További programokat tudsz írni magadnak, a leírt szerkezettel összhangban, és egy charthoz csatolva futtatni is tudod őket. Próbálj megérteni minden fogalmat és szabályt, és az MQL4 programok létrehozása könnyű és kellemes lesz

77 Példák a megvalósításra Az előző részben elemeztünk egy példát a különleges függvények végrehajtására az egyszerű simple.mq4 Expert Advisor segítségével. Gyakorlásképpen elemezzük ennek a programnak még néhány módosítását! Példa egy helyes programszerkezetre Általános szabály, hogy a függvényleírásokat ugyanabban a sorrendben írják le a programban, ahogyan őket az ügyfélterminál végrehajtásra hívja, mégpedig először az init() különleges függvény leírása, azután a start() és végül a deinit() következik. Azonban különleges függvények végrehajtása a tulajdonságaik alapján, és nem a programban elfoglalt helyük alapján történik. Változtassuk meg a függvényleírások sorrendjét és lássuk az eredményt (Expert Advisor possible.mq4). // // possible.mq4 // To be used as an example in MQL4 book. // int Count=0; // Global variable // int start() // Special funct. start() double Price = Bid; // Local variable Count++; Alert("New tick ",Count," Price = ",Price);// Alert return; // exit start() // int init() // Special funct. init() Alert ("Function init() triggered at start");// Alert return; // Exit init() // int deinit() // Special funct. deinit() Alert ("Function deinit() triggered at exit");// Alert return; // Exit deinit() // Indítsd el ezt az Expert Advisor és látni fogod, hogy a különleges függvények védrehajtási sorrendje egy programban nem függ a leírásuk sorrendjétől. Megváltoztathatod a függvényleírások pozícióit egy forráskódban és az eredmény ugyanaz lesz, mint a simple.mq4 Expert végrehajtásában. Példa a helytelen programszerkezetre Azonban a program másképp fog viselkedni, ha megváltoztatjuk a fejrész pozícióját. A példánkban start() függvényt a fejrész elé tesszük (incorrect.mq4 Expert Advisor): // // incorrect.mq4 // To be used as an example in MQL4 book. // int start() // Special funct. start() double Price = Bid; // Local variable Count++; Alert ("New tick ",Count," Price = ",Price);// Alert return; // Exit start()

78 // int Count=0; // Global variable // int init() // Special funct. init() Alert ("Function init() triggered at start");// Alert return; // Exit init() // int deinit() // Special funct. deinit() Alert ("Function deinit() triggered at exit");// Alert return; // Exit deinit() // Amikor megpróbálja lefordítani a Meta Editor ezt az Expert Advisort, egy hibaüzenetet fog küldeni: Az ebben a sorban: 36. ábra. Hibaüzenet az incorrect.mq4 fordítása alatt. int Count=0; // Global variable deklarált változó minden függvényen kívül van ugyan, de nem a program kezdetén, hanem valahol a közepén. A meghatározó momentum a programszerkezetben az, hogy a Count globális változó deklarációja egy függvény után van (ez esetben a start() különleges függvény után). Ebben a részben nem tárgyaljuk a globális változók használatának a részleteit; a változók fajtáit és használatuk szabályait a Változók részben fogjuk leírni. Most csak azt kell tudnunk, hogy bármilyen globális változót korábban kell deklarálni (a szövegben korábban említeni) ezen változó első használatánál (ez a start() függvényben van). Az elemezett programban ezt a szabályt megszegtük és a szerkesztő egy hibaüzenetet küldött. Példa felhasználói függvény használatára És most lássuk, hogy a program hogyan viselkedik felhasználói függvényekkel. Ezért írjunk egy egyszerű Expert Advisor kódot a simple.mq4 példája alapján, majd elemezzük azt. Ez a program felhasználói függvényekkel így fog kinézni (Expert Advisor userfunction.mq4): // // userfunction.mq4 // To be used as an example in MQL4 book. // int Count=0; // Global variable // int init() // Special funct. init() Alert ("Function init() triggered at start");// Alert return; // Exit init() // int start() // Special funct. start() double Price = Bid; // Local variable

79 My_Function(); // Custom funct. call Alert("New tick ",Count," Price = ",Price);// Alert return; // Exit start() // int deinit() // Special funct. deinit() Alert ("Function deinit() triggered at exit");// Alert return; // Exit deinit() // int My_Function() // Custom funct. description Count++; // Counter of calls // Először is lássuk, hogy mi maradt változatlan. Változatlan részek: 1. A fejrész változatlan. // userfunction.mq4 // To be used as an example in MQL4 book. // int Count=0; // Global variable 2.Az init() különleges függvény változatlan. int init() // Special funct. init() Alert ("Function init() triggered at start");// Aler return; // Exit init() 3.A deinit() különleges függvény is változatlan. int deinit() // Special funct. deinit() Alert("Function deinit() triggered at exit"); // Alert return; // Exit deinit() A változások: 1. Hozzáadva: a My_Function() felhasználói függvény. int My_Function() // Custom function description Count++; // Counter of calls 2. A start() különleges függvény kódja szintén változott: most ez tartalmazza a felhasználói függvényhívást, de a Count változó számítása nincs benne. int start() // Special funct. start() double Price = Bid; // Local variable My_Function(); // Custom function call Alert("New tick ",Count," Price = ",Price);// Alert return; // Exit start() A Program végrehajtás részben az init() és deinit() végrehajtási rendjét elemeztük. Ebben a példában ezeket a függvényeket ugyanúgy fogják végrehajtani, tehát most nem fogunk elidőzni velük. Elemezzük a start() különleges függvény végrehajtását és a My_Function() felhasználói függvényt. A felhasználói függvényleírásnak minden különleges függvényen kívül kell lennie, ezt a követelményt teljesítettük. A felhasználói függvényhívás a start() kódjában van, ami szintén helyes

80 Az init() végrehajtása után a végrehajtás folytatódik: 31.a start() különleges függvény arra vár, hogy elindítsa az ügyfélterminál. Amikor egy új tick jön, a terminál el fogja indítani ezt a függvényt. Végül a következő műveleteket fogja végrehajtani: 32 (1). Ebben a sorban double Price = Bid; // Local variable ugyanazokat a műveleteket hajtja végre: 32.1(1). Helyi változót inicializálják (lásd: A változók típusai). Ennek a lokális változónak az értéke elérhető lesz a start() különleges függvény bármilyen részéről. 32.2(1). Az értékadó operátort végrehajtják. Az utolsó elérhető árajánlatot fogják adni a változó értékének (például az első ticknél ez egyenlő gyel). 33(1). Aztán jön a My_Function() hívása: My_Function(); // Custom function call Ezt a sort végre fogják hajtani a start() függvényen belül. Ezen kódrész végrehajtásnak az eredménye (felhasználói függvényhívás) a vezérlés átadása a függvény törzsre (függvényleírás) majd visszatérése a hívás helyére. 34(1). Csak egy operátor van a felhasználói függvényleírásban: Count++; Az első függvényhívásnál a Count egyenlő nullával. A Count++ operátor végrehajtásának eredményeként a Count értéke eggyel növekszik. Miután egyszer végrehajtotta ezt az operátort, a felhasználói függvény befejezi a működését és a vezérlést visszaküldi arra a helyre, ahonnan a függvényhívást kapta. Tudni kell, hogy a felhasználói függvényeket csak a különleges függvényekből lehet hívni (vagy olyan másik felhasználói függvényből, amelyet különleges függvényből hívtak). Bármelyik aktuális pillanatban a különleges függvények közül az egyik működik (vagy a start() vár egy új tickre és akkor elindítja az ügyfélterminál) és a felhasználói függvényeket csak a különleges függvényekben hajthatjuk végre. Jelen esetben a vezérlés visszatér a start() különleges függvényhez és az végrehajtja a függvényhívás utáni sort: 35(1). Ez a sor az Alert() hívását tartalmazza: Alert ("New tick ",Count," Price = ",Price);// Alert Az Alert() függvény egy ablakban mutatni fog minden állandó és változó, amelyeket a zárójeleiben felsoroltak: New tick 1 Price = (1). A return return; // Exit start() operátor befejezi a start() függvény működését. 37. A vezérlés átkerül az ügyfélterminálra, ami vár egy új tickre. A start() további végrehajtásainál a változók új értékeket fognak kapni és az Alert() üzeneteket fog mutatni, vagyis a program a pontokat fogja végrehajtani. A start() mindegyik végrehajtásnál (mindegyik ticknél) hívja a My_Functiont felhasználói függvényt és ezt a függvényt végre fogja hajtani. Start() végrehajtása ismétlődni fog, amíg a felhasználó úgy nem dönt, hogy befejezi a programot. Ebben az esetben a deinit() különleges függvény lesz végrehajtva és a program abba fogja hagyni működés. Az userfunction.ех4 program, működése során, mutatni fog egy Alert() ablakot, ami üzeneteket tartalmaz. Figyelem, a program futásának az eredménye ugyanaz lesz mint az egyszerű simple.mq4 Expert Advisor eredménye. Világos, mert az userfunction.mq4 ugyanazokat a szerkezeti blokkokat tartalmazza, csak más elrendezésben. Ha egy másik, megfelelő elrendezést használnánk, az eredmény ugyanez lenne

81 Operátorok Ez a rész az MQL4-ben használt operátorok formai szabályaival és végrehajtásával foglalkozik. Mindegyik rész egyszerű példákat tartalmaz, amik demonstrálják az operátorok végrehajtását. A teljes anyag megemésztése érdekében azt ajánljuk, hogy állíts össze minden példaprogramot és futtasd őket. Ez segíteni fog abban, hogy a Meta Editorral kapcsolatos ismereteid megszilárdítsd. Értékadó operátor. Ez a legegyszerűbb és a legintuitívebb operátor. Mindannyian ismerjük az értékadó operátort az iskolai matematikától: Egy egyenlőségjel bal oldalán lévő változóhoz hozzárendeljük az egyenlőségjel jobb oldalán lévő értéket. 'if-else' feltételes operátor. Gyakran szükséges a programot bizonyos feltételek figyelembevételével irányítani egyik vagy másik irányba. Ezekben az esetekben az "if-else" operátor nagy segítséget jelent. 'while' ciklus operátor. A nagy, tömb típusú adatsorok feldolgozása általában a műveletek többszörös ismétléseket követelik meg. Az ilyen műveletek végrehajtását a while" ciklusoperátorban lehet megszervezni. Minden egyes ciklusvégrehajtást iterációnak (ismétlés) nevezünk. 'for' ciklus operátor. A "for" operátor szintén egy ciklusoperátor. Azonban a "while" operátortól eltérően itt megadjuk az ismétlések végrehajtásának a feltételeit, kezdő és végső értékét. 'break'operátor. Ha meg akarod szakítani egy ciklusoperátor működését a hátralévő ciklusuk végrehajtása nélkül, akkor a "break" operátorra van szükséged. Ezt az operátort csak a "while", "for" és switch"operátorokban használjuk. 'continue' operátor. Egy nagyon hasznos operátor - az operátor a cikluson belül a következő ismétlésre lép. Lehetővé tesz a programnak, hogy kihagyja az inaktív operátorokat az aktuális ciklusban és a következő ciklusra lépjen. 'switch' operátor. Ez az operátor egy olyan váltókapcsoló, ami lehetővé teszi a programnak, hogy a sok lehetséges alternatíva közül az egyiket válassza. Mindegyik alternatívát egy előre definiált állandó ír le, és ha az alternatívára előírt követelmény teljesül, akkor az az alternatíva történik. Függvényhívás. Függvényhívás alatt azt értjük, amikor a hívott függvény végre fog hajtani néhány műveletet. A függvény visszaküldheti az előre definiált típusú változó értékét. Az átadott függvényparaméterek száma nem lehet több 64-nél. Függvény leírás és a 'return' operátor. Ahhoz, hogy hívni lehessen egy felhasználói függvényt, először le kell írni azt. A függvényleírás részletezi a típusát, nevét és a paraméterek listája. Azonkívül a függvénytörzs végrehajtható operátorokat tartalmaz. Egy függvény végrehajtását a return operátor fejezi be

82 Értékadó operátor Az értékadó operátor a legegyszerűbb és a legtöbbször használt operátor. Az értékadó operátor formátuma Értékadó operátor egy olyan rekord, ami = (egyenlőségjel) karaktert tartalmaz. Ennek az egyenlőségjelnek a bal oldalán megadjuk egy változó nevét, a jobb oldalán megadunk egy kifejezést. Az értékadó operátort ; (pontosvessző) fejezi be. Változó = Kifejezés; // Értékadó operátor Az értékadó operátort a program szövegében az egyenlőségjel jelenlétéből lehet felismerni. A kifejezésben megadható: egy állandó, egy változó, egy függvényhívás vagy egy kifejezés. Az értékadó operátor végrehajtása Számítsd ki a kifejezés értékét az egyenlőségjeltől jobbra és add a kapott értéket az egyenlőségjel bal oldalán lévő változónak. Az értékadó operátor, mint bármilyen másik operátor, végrehajtható. Ez azt jelenti, hogy a rekordot, ami alkotja az operátort, a végrehajtási szabályai szerint végrehajtják. Miközben végrehajtjuk az operátort, a jobb oldali rész kiszámított értékét adjuk az egyenlőségnek bal oldalán levő változónak. A értékadó operátor végrehajtásának eredményeképpen a bal oldali részben lévő változó egy új értéket vesz fel, ez az érték ugyanaz vagy más is lehet, mint a változó előző értéke. Az értékadó operátor jobb oldalán lévő kifejezést a műveletek sorrendje szerint kiszámoljuk ki (lásd: Műveletek és kifejezések). Példák értékadó operátorokra Egy értékadó operátorban lehetőség van a változó típusát deklarálni az egyenlőségjel bal oldalán: int In = 3; // The constant value is assigned to variable In double Do = 2.0; // The constant value is assigned to variable Do bool Bo = true; // The constant value is assigned to variable Bo color Co = 0x008000; // The constant value is assigned to variable Co string St = "sss"; // The constant value is assigned to variable St datetime Da= D' ';// The constant value is assigned to variable Da A korábban már deklarált (létrehozott) változókat használhatjuk egy értékadó operátorban anélkül, hogy meghatároznánk a típusukat. In = 7; // The constant value is assigned to variable In Do = 23.5; // The constant value is assigned to variable Do Bo = 0; // The constant value is assigned to variable Bo Egy értékadó operátorban a változó típusát nem deklarálhatjuk az egyenlőségjel jobb oldalán: In = int In_2; // Variable type may not be declared in the right part Do = double Do_2; // Variable type may not be declared in the right part Egy értékadó operátorban egy változó típusát nem szabad ismételten deklarálni. int In; // Declaration of the type of variable In int In = In_2; // The repeated declaration of the type of the variable (In) is not allowed Példák arra, hogy hogyan használjuk a felhasználói és beépített függvényeket a jobb oldalon:

83 In = My_Function(); // The value of user-defined function is assigned to variable In Do = Gipo(Do1,Do1); // The value of user-defined function is assigned to variable Do Bo = IsConnected(); // The value of standard function is assigned to variable Bo St = ObjectName(0); // The value of standard function is assigned to variable St Da = TimeCurrent(); // The value of standard function is assigned to variable Da Példa a jobb oldalon kifejezések használatára: In = (My_Function()+In2)/2; // The variable In is assigned //..with the value of expression Do = MathAbs(Do1+Gipo(Do2,5)+2.5); // The variable Do is assigned //..with the value of expression Az értékadó operátorokban lévő számításokban a typecasting (tipusmódosítás) szabályai alkalmazandók (lásd Typecasting). Példák a rövid alakban írt értékadó operátorra Az MQL4-ben az értékadó operátor írásának rövidített formáját is használják. (Az értékadó operátor e formáit lásd: Műveletek és kifejezések). Az operátorok rövidített alakjára ugyanazok a szabályok és korlátozások vonatkoznak. A értékadó operátorok rövid fajtáját a jobb megjelenítés miatt használják a kódban. A programozónak lehetősége van az értékadó operátor mindkét formájának az alkalmazására. Bármelyik, a rövid vagy a teljes formátum alkalmazása esetén az operátor végrehajtásának az eredménye változatlan. In /= 33; // Short form of the assignment operator In = In/33; // Full form of the assignment operator St += "_exp7"; // Short form of the assignment operator St = St + "_exp7"; // Full form of the assignment operator

84 Az 'if-else' feltételes operátor Ha egy alkalmazási programot írsz akkor lehetséges, hogy egy programban több megoldás közötti választást kell kódolnod. Az ilyen feladatok megoldásához a kódodban az if-else feltételes operátort használhatod. Az if-else operátor formátuma Teljes formátum Az if-else operátor teljes formátuma tartalmaz egy fejlécet a feltétellel, az 1. operátortörzset az 'else' kulcsszóval, és a 2. operátortörzset. Az operátortörzsek egy vagy több operátorból állhatnak; az operátortörzseket kapcsos zárójelek határolják. if ( Feltétel ) // Az operátor fejléce a feltétellel Az 1. operátor blokk // Ha a Feltétel igaz(true), akkor.. alkotja az 1. operátortörzset //..az 1. operátor blokk kerül végrehajtásra else // Ha a Feltétel hamis(false) akkor.. akkor. A 2. operátor blokk //..a 2. operátor blokk kerül.. alkotja a 2. operátortörzset //..végrehajtásra Formátum az 'else' nélkül Az if-else operátor használható az 'else' rész nélkül is. Ebben az esetben, az if-else operátor tartalmazza a fejlécét a feltétellel és az 1. operátortörzset, amely kapcsos zárójelekkel határolt és egy vagy több operátorból áll. if ( Feltétel ) // Az operátor fejléce a feltétellel Az 1. operátor blokk // Ha a Feltétel igaz(true), akkor.. alkotja az 1. operátortörzset //..az 1. operátor blokk kerül végrehajtásra Formátum a kapcsos zárójelek nélkül Ha az if-else operátortörzs csak egy operátorból áll, a kapcsos zárójelek elhagyhatók. if ( Feltétel ) // Az operátor fejléce a feltétellel Operator; // Ha a feltétel igaz ez az operátor végrehajtódik. Az if-else operátor működési szabályai Ha az if-else operátor feltétele igaz, az 1. operátortörzsben megkapja a vezérlést az első operátor. Az 1. törzsben lévő összes operátor végrehajtsa után a vezérlést átadja annak az operátornak, amelyik az if-else operátort követi. Ha az if-else operátor feltétele hamis, akkor: - ha ott van az 'else' kulcsszó az if-else operátorban, a 2. törzsben megkapja a vezérlést az első operátor. Az 2. törzsben lévő összes operátor végrehajtsa után a vezérlést átadja annak az operátornak, amelyik az if-else operátort követi. - ha az 'else' kulcsszó nincs ott az if-else operátorban, a vezérlést átadja annak az operátornak, amelyik az if-else operátort követi

85 Példák az if-else operátor működésére Találjunk néhány olyan példát, ahol be tudjuk mutatni az if-else operátor használatát. 9. feladat: Alkoss egy olyan programot, ahol a következő feltételek teljesülnek: Ha egy szimbólum ára átlépett egy bizonyos értéket, a programnak erről tájékoztatnia kell a kereskedőt; ha nem lépte át ezt az értéket, a programnak nem szabad csinálnia semmit. Egyféle megoldás lehet erre a problémára például következő : (onelevel.mq4): // // onelevel.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' double Level, // Alert level Price; // Current price Level=1.2753; // Set the level Price=Bid; // Request price // if (Price>Level) // Operator 'if' with a condition Alert("The price has exceeded the preset level");// Message to the trader // return; // Exit start() // Először is ismerjük fel, hogy ezt a programot Expert Advisorként hozták létre. Ez arra utal, hogy a program hosszú időn át működni fog azért, hogy a szükséges üzenetet be tudja mutatni a képernyőn, amint az ár átlépi az előre beállított szintet. A programnak csak egy különleges függvénye van, a start(). A függvény kezdetén a változókat deklarálják és magyarázzák. Azután megadják a riasztási árszintet és az aktuális árat kérik. Az if-else operátort a programban a következő sorokban használják: // if (Price>Level) // Az 'if' operátor a feltétellel Alert("The price has exceeded the preset level");// Message to the trader // Amint a program átadja a vezérlést az if-else operátornak a program tesztelni fogja a feltételt. Jegyezd meg, hogy a feltétel vizsgálata az if-else operátorban ennek az operátornak a jellemző tulajdonsága. Ezt a vizsgálatot nem hagyhatja ki az if-else operátor a végrehajtása alatt, ez egy elengedhetetlen követelmény, amit az operátor minden esetben végre fog hajtani. Majd ennek a vizsgálatnak az eredménye alapján fogja a vezérlést továbbadni valamelyik operátortörzsnek vagy a következő külső operátornak

86 A 37. ábrán láthatunk egy blokkdiagramot, az if-else operátor egy lehetséges végrehajtásával. 37. ábra: blokkdiagram az if-else operátor végrehajtásával az onelevel.mq4 programban. Ebben és minden következő diagramban egy rombusz képviseli a feltétel vizsgálatát. A nyilak mutatják a az aktuális blokk végrehajtása után vezérlés átadás irányát. Vizsgáljuk meg a diagramot alaposan! A Előző számítás blokk (szürke téglalap a diagramban) az onelevel.mq4 programban, a következőket tartalmazza: double Level, // Alert level Price; // Current price Level=1.2753; // Set the level Price=Bid; // Request price Miután az utolsó operátort ebben a blokkban végrehajtották, vezérlést átadják az if-else operátor fejlécébe hol a feltételt ellenőrzik: az ár felülmúlja az előre beállított szintet?(rombusz 37. ábra diagramjában). if (Price>Level) // Operator 'if' with a condition Más szóval azt mondhatjuk, hogy a program ezen szakaszában következő kérdésre keressük a választ: A zárójelben lévő kijelentés igaz? Maga a kijelentés szó szerint így hangzik: A Price változó értéke nagyobb, mint a Level változó (az ár meghaladta az előre beállított szintet). Az ellenőrzés pillanatában a program már ismerte a Price és Level változók számszerű értékeit. A válasz ezen értékek viszonylatától függ. Ha az ár az előre beállított szint alatt van (az ár értéke kevesebb vagy egyenlő a Level értékénél), a kijelentés hamis; ha az ár felülmúlja ezt a szintet, a kijelentés igaz. Hová kerül a vezérlés, miután a feltétel eredménye az aktuális piaci helyzettől függ (!). Ha egy szimbólum ára az előre beállított szint alatt marad (a válasz, Nem, vagyis a kijelentés hamis), az vezérlés az if-else operátor végrehajtási szabálya szerint, az operátoron kívül fog kerülni ebben az esetben a Következő számítás blokkba, mégpedig a következő sorba: return; // Exit start() Könnyű belátni, hogy a kereskedő nem kapott üzenetet. Ha egy szimbólum ára meghaladja az előre beállított szintet a programban (a válasz Igen, a kijelentés igaz), a vezérlés át fog kerülni az if-else operátor törzsébe, mégpedig a következő sorba: Alert("The price has exceeded the preset level");// Message to the trader Az Alert() függvény végrehajtása nyitni fog a képernyőn egy riasztási ablakot, ami a következő üzenetet tartalmazza:

87 The price has exceeded the preset level Az Alert() függvény az egyetlen operátor az if-else operátor törzsében, ezért az Alert() függvény végrehajtása után, az if-else operátor teljesen végrehajtódott, és a vezérlés átkerül arra az operátorra, ami az if-else operátort követi: return; // Exit start() Az 'return' operátor végrehajtása azzal végződik, hogy a start() függvény befejezi a munkáját, és a program tick váró üzemmódra vált. Egy új ticknél (ami a szimbólum árváltozását jelenti), a start() függvényt megint végre fogják hajtani. Az üzenet, amit a programban kódoltak attól függően fog megjelenni, hogy az új ár meghaladja-e az előre beállított szintet. Az if-else operátorokat egymásba lehet illeszteni. Az egymásba ágyazott operátorok használatának bemutatására vizsgáljuk meg a következő példát. Ez a probléma kicsit bonyolultabb. 10 feladat: Alkoss egy olyan programot, ahol a következő feltételek teljesülnek: Ha az ár növekszik és meghalad egy bizonyos 1. szintet, a programnak erről tájékoztatnia kell a kereskedőt; ha az ár csökkent és egy bizonyos 2. szintnél alacsonyabbá vált, a programnak erről is tájékoztatnia kell a kereskedőt, azonban a programnak más esetben nem szabad végrehajtania semmilyen műveletet. A feladat egyértelmű, azért, hogy megoldjuk ezt a problémát kétszer kell ellenőriznünk az aktuális árat: 1. összehasonlítjuk az árat az 1. szinttel, és 2. összehasonlítjuk a 2. szinttel. A 10. feladat 1. megoldása: A szabályok figyelembevételével, a következő megoldási algoritmust készíthetjük: 38. ábra: blokkdiagram az if-else i operátorok végrehajtására a twolevel.mq4 programban

88 A programot, ami megvalósítja ezt az algoritmust itt tudjuk követni: (twolevel.mq4): // // twolevel.mq4 // The code should be used for educational purpose onlyl. // int start() // Special function 'start' double Level_1, // Alert level 1 Level_2, // Alert level 2 Price; // Current price Level_1=1.2850; // Set level 1 Level_2=1.2800; // Set level 2 Price=Bid; // Request price // if (Price > Level_1) // Check level 1 Alert("The price is above level 1"); // Message to the trader // if (Price < Level_2) // Check level 2 Alert("The price is above level 2"); // Message to the trader // return; // Exit start() // Könnyű észrevenni, hogy a twolevel.mq4 program kódja az onelevel.mq4 program kiterjesztett verziója. Ha az előző számításoknak csak egy szintje volt, ebben az új programban nekünk két szintünk van. Mindkét szintet számszerűen meghatároztuk, és a feladat megoldásához a programnak két olyan blokkja van ahol figyeljük az árváltozást: Marad e az árat az előre beállított szintek tartományán belül, vagy ezen a tartományon kívül van? Megadjuk a programvégrehajtás rövid leírását! Miután az előző számításokat végrehajtása megtörtént a vezérlést megkapja az első if-else operátor: // if (Price > Level_1) // Checking the first level Alert("The price is above level 1"); // Message to the trader // Ezen operátor végrehajtásának bármi is lesz a lefolyása (a kereskedő kap-e üzenetet vagy sem), a végrehajtás átkerül a következő if-else operátorhoz: // if (Price < Level_2) // Checking the second level Alert("The price is below level 2"); // Message to the trader // A két operátor egymást követő végrehajtása mindkét feltételes tesztelésével végződik, ezen operátorok végrehajtása vezet a probléma megoldásához. Bár a program teljesen megoldja a feladatot, ezt a megoldást nem fogadhatjuk el tökéletesnek. Egy nagyon fontos részletet vegyünk észre: A második feltétel vizsgálatát az első blokk eredményére való tekintet nélkül végre fog hajtódni. A második blokk még akkor is végrehajtásra kerül, ha az ár meghaladja az első szintet

89 Itt kell megjegyezni, hogy a program írásakor a legfontosabb szempontok egyike az algoritmus optimalizálása. A fenti példák éppen ezt demonstrálják, ezek rövid, gyorsan végrehajtható programok. Általában egy gyakorlatban használt program ezeknél sokkal nagyobb. Ezek több száz változó értékét használhatják, többszörös teszteket végezhetnek, mindegyik végrehajtása bizonyos mennyiségű időt követel. Mindez azzal a kockázattal jár, hogy a start() különleges függvény működésének az időtartama felülmúlhatja a tickek közötti időtartamot. Ez azt jelenti, hogy néhány tick feldolgozás nélkül maradhat. Ez természetesen egy nemkívánatos helyzet, és a programozónak mindent meg kell tennie azért, hogy megakadályozza ezt. Aprogramvégrehajtás idejének csőkkentésére az egyik módszer az algoritmus optimalizálása. Fontoljuk meg a legújabb példát még egyszer, hogy az mennyire takarékos az erőforrások tekintetében. Például, miért kérdezzük meg, hogy az ár a 2. szint alá csökkent? ha azt az első blokk már érzékelte, hogy az ár az 1. szint fölött van? Világos, hogy ebben az esetben az ár nem lehet az alacsonyabb 2. szintnél, úgyhogy nem szükséges ezt a második blokkban vizsgálni (nem szükséges végrehajtani az operátorokat ebben a blokkban). A 10 feladat 2. megoldása A fenti érvelést figyelembe véve, vizsgáljuk meg a következő optimalizált algoritmust: 39. ábra: blokkdiagram az if-else operátor végrehajtására a twoleveloptim.mq4 programban. Ez összhangban van az előző blokkdiagramban vázolt algoritmussal, azonban redundáns műveleteket nem fogunk végrehajtani a programban. Az előkészítő számítások után a program tesztelni fogja, hogy az ár az előre beállított szint fölött van-e. Ha igen, a program a megfelelő üzenetet mutatja a kereskedőnek és átadja a vezérlést a Következő számítás blokknak és a második feltételt (az ár alacsonyabb-e az előre beállított szintnél?) nem teszteli. Csak ha az árról nem derült ki, hogy a felső szint fölött van (a válasz, No), akkor kerül a vezérlés második blokkba, ahol a szükséges feltételes tesztet végrehajtjuk. Ha az árról kiderül, hogy az alacsonyabb szint alatt van, a megfelelő üzenetet fogja kapni a kereskedő. Ha nem, a vezérlés a további számításokra lép. Nyilvánvaló, ha a program ezzel az algoritmussal összhangban működik, redundáns műveleteket nem hajt végre, és ez az erőforrások jelentős megtakarításával jár. Ennek az algoritmusnak a programozott megvalósítása (twoleveloptim.mq4) szemlélteti az egymásba ágyazott if-else operátorok használatát:

90 // // twoleveloptim.mq4 // The code should be used for educational purpose only. // int start() // Special function start() double Level_1, // Alert level 1 Level_2, // Alert level 2 Price; // Current price Level_1=1.2850; // Set level 1 Level_2=1.2800; // Set level 2 Price=Bid; // Request price // if (Price > Level_1) // Check level 1 Alert("The price is above level 1"); // Message to the trader else if (Price < Level_2) // Check level 2 Alert("The price is above level 2"); // Message to the trader // return; // Exit start() // Figyeld meg a számításoknak ezt a blokkját: // if (Price > Level_1) // Check level 1 Alert("The price is above level 1"); // Message to the trader else if (Price < Level_2) // Check level 2 Alert("The price is above level 2"); // Message to the trader // Az az if-else operátor, amiben a második feltételt tesztelik, annak az első if-else operátor egy összetevője, amiben az első feltételt tesztelik. Egymásba ágyazott operátorokat a programozói gyakorlatban széles körben használnak. Ez gyakran ésszerű és néhány esetben ez az egyetlen lehetséges megoldás. Az Alert() függvény helyett az igazi programokban más függvényeket vagy operátorokat használhatnak, amelyek különféle hasznos műveleteket hajtanak végre. Egy összetett kifejezést szintén használhatnak feltételként az if-else operátorban. 11. feladat: alkoss egy olyan programot, ami megvalósítja a következő feltételeket: Ha az ár értéke az előre beállított tartományán belül van, nem kell riasztani; ha az ár ezen a tartományon kívül kerül, a programnak erről tájékoztatnia kell a kereskedőt. Ennek a feladatnak a megfogalmazása hasonló a 10 feladathoz. A különbség az, hogy ebben az esetben nem fontos az ármozgás iránya csak az, hogy az előre definiált tartományon belül van-e. A problémafelvetés szerint nekünk csak magát a tényt kell tudnunk: Az ár a tartományon belül vagy kívül van? Most ugyanazt a kódot használhatjuk csak más üzenet szöveggel

91 Itt is, mint az előző megoldásokban ellenőrizni kell az árstátuszt a felső és az alsó szintre vonatkozóan. A 38. ábrán szereplő megoldás algoritmust elvetettük, mert az nem hatékony. A fenti érvelés alapján javasoljuk a következő megoldást: 40. ábra. 11. feladat egyik megoldásának a blokkdiagramja. Azonban nem szükséges ezt az algoritmust használni. A diagram megmutatja, hogy az algoritmus a programban különböző pontoknál utal ugyanannak az üzenetnek a használatára. A programsorokat ismételni fogjuk, annak ellenére, hogy a végrehajtásuk ugyanazokat az üzeneteket fogja eredményezni. Ebben az esetben sokkal hatékonyabb volna használni az egyetlent if-else operátor egy összetett feltétellel: 41. ábra: blokkdiagram az if-else operátor végrehajtására a compoundcondition.mq4 programban. Annak a programnak a kódja, ami megvalósítja ezt az algoritmust, a következő (compoundcondition.mq4):

92 // // compoundcondition.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' double Level_1, // Alert level 1 Level_2, // Alert level 2 Price; // Current price Level_1=1.2850; // Set level 1 Level_2=1.2800; // Set level 2 Price=Bid; // Request price // if (Price>Level_1 Price<Level_2) // Test the complex condition Alert("The price is outside the preset range");// Message // return; // Exit start() // Ez a programmegoldás betekintést enged az összetett feltételek használatába az if-else operátorban: if (Price>Level_1 Price<Level_2) // Test the complex condition Alert("The price is outside the preset range");// Message Egyszerűen megfogalmazva, ez a feltétel következő: Ha a Price változó értéke magasabb, mint a Level_1változó, vagya Price változó értéke alacsonyabb, mint a Level_2 változó, a programnak végre kell hajtania az if-else operátor törzsét. Megengedett a logikai műveletek használata (&&, and!) az if-else operátor feltételei meghatározásakor, ezeket széles körben használják a programozói gyakorlatban (lásd Boolean (Logikai) müveletek). A problémamegoldás során lehet, hogy neked még összetettebb feltételeket is lérte kell hoznod. Néhány kifejezést, köztük az egymásba ágyazottakat is, zárójelbe tehetünk. Például feltételként használhatod a következő kifejezést az if-else operátorban: if ( A>B && (B<=C (N!=K && F>B+3)) ) // Example of a complex condition Tehát az MQL4 sok lehetőséget nyújt az if-else operátorok használatára: be tudjuk illeszteni őket, egymásba ágyazott szerkezeteket tudunk szerkeszteni, egyszerű és összetett tesztfeltételeket tudunk használni, melyek azt a lehetőséget biztosítják, hogy bonyolult algoritmusokat alkossunk a programjainkban

93 A while ciklus operátor Az MQL4-ben igen hasznos lehetőség, hogy benne ciklusokat (hurkokat) szervezhetünk. Az alkalmazási programok létrehozása közben gyakran használunk ismételt számításokat, melyek javarészt ismétlődő programsorok. Hogy kényelmessé tegyük a programozást, és maga a program is felhasználóbarát legyen ciklus operátorokat használhatunk.az MQL4-ben két ciklusoperátor van: while és for. Ebben a részben az elsőt tárgyaljuk. A while operátor formátuma A while ciklus-operátor teljes formátuma a fejlécből, -ami egy feltételt tartalmaz, és a kapcsos zárójelek közé zárt végrehajtható ciklustörzsből áll. while ( Feltétel ) // A ciklusoperátor fejléce // Kapcsos zárójel Operátor blokk alkotja.. // Az operátor törzs több operátort....a ciklus törzset //.. is tartalmazhat // Záró kapcsos zárójel Ha az operátortörzs csak egy operátorból áll a while operátorban a kapcsos zárójelek elhagyhatók. while ( Feltétel ) // A ciklusoperátor fejléce Csak egy operátor van a ciklustörzsben // A ciklustörzs csak egy operátort tartalmaz A while operátor végrehajtás szabályai Ameddig a while operátor feltétele igaz: A program átadja a vezérlést a ciklustörzs operátorainak; miután a ciklustörzsben minden operátort végrehajtottak, ez átadja a vezérlést a fejlécbe, hogy az tesztelje a feltétel igazságát. Ha a while operátor feltétele hamis, a vezérlést át kell adni annak az operátornak amelyik a while ciklusoperátort követi. Vizsgáljunk meg egy példát! 12. feladat: számoljuk ki a Fibonacci együtthatót 10 számjegy pontossággal. Először is, röviden ismerjük meg a Fibonacci együtthatót. Leonardo Fibonacci olasz matematikus, felfedezett egy különös számsorozatot: Ebben a számsorozatban mindegyik szám az őt megelőző két szám összege. Ennek a számsorozatnak van néhány egyedülálló tulajdonsága: A sorozatban levő két egymás utáni szám aránya egyenlő cal, míg az arány egy szám és az előző szám között egyenlő cal. Az arányt Fibonacciról nevezték el, és a fenti sorozatot Fibonacci sorozatnaknevezték el(azt is meg kell jegyezni, hogy a a konjugált Fibonacci szám, amit akkor kapunk, ha azt megszorozzuk önmagával: = х 0.618)

94 A feladatnak nagy pontossággal kell ki számolnia a Fibonacci számot. Ha a Fibonacci együtthatót a Fibonacci sorozat több tíz elemére elemezzük, akkor nyilvánvalóvá válik, hogy a kapott együtthatók az irracionális számhoz alulról és felülről közelítenek. Ha a sorozat növekvő számain végezzük a számításokat, az eredmény eltérése ettől az értéktől egyre kisebb. Nem tudjuk előre, hogy a Fibonacci sorozat mely elmei azok a számok, ahol a számított együtthatók csak a tizedik számjegy után különböznek. Tehát egy olyan programot kell létrehozni, amely addig keres az együtthatókban, amíg ( while ) az együtthatók közti különbség nél kevesebbet nem lesz. Ebben az esetben egy scriptet kell írni (fibonacci.mq4) a 12. feladat megoldására, mert a programot nem fogjuk állandóan használni, nem szükséges, hogy folyamatosan működjön. Egyszer csatoljuk a szimbólumablakhoz, a script elvégez minden szükséges számítást (az eredmény bemutatását beleértve), azután azt az ügyfélterminál el fogja távolítani az ablakból. // // fibonacci.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int i; // Formal parameter, counter double A,B,C, // Numbers in the sequence Delta, // Real difference between coefficients D; // Preset accuracy // A=1; // Initial value B=1; // Initial value C=2; // Initial value D= ; // Set accuracy Delta=1000.0; // Initial value // while(delta > D) // Cycle operator header // Opening brace of the cycle body i++; // Counter A=B; // Next value B=C; // Next value C=A + B; // Next value Delta=MathAbs(C/B - B/A); // Search difference between coefficients // Closing brace of the cycle body // Alert("C=",C," Fibonacci number=",c/b," i=",i);//display on the screen return; // Exit start() // A program kezdetén a változókat deklaráljuk (és magyarázzuk). A következő sorokban az A, B és C változók számszerű értékeit adják Fibonacci sorozat a kezdeti értékét. Itt tudni kell azt, hogy maga a sorozat egész számokat tartalmaz, csak a hányadosuk valós szám (tört szám). Ha a hányadosra az int típust használnánk, lehetetlen volna kiszámolni Fibonacci együtthatót, például: 8/5 = 1 (egész szám 1, a tört rész nélkül). Úgyhogy ebben az esetben double típusú változókat használunk. A ciklusoperátor így néz ki: while(delta > D) // Cycle operator header // Opening brace of the cycle body i++; // Counter A=B; // Next value B=C; // Next value C=A + B; // Next value

95 Delta=MathAbs(C/B - B/A); // Search difference between coefficients // Closing brace of the cycle body Figyeljük meg a while ciklus operátor blokkdiagramját: 42. ábra: A while operátor végrehajtásának blokkdiagramja a fibonacci.mq4 programban. Induláskor a ciklus operátor teszteli a feltétel. A ciklust ismételten végre fogja hajtani amíg ( while ) a feltétel (Delta>D) igaz. Sokkal jobb megfogod érteni programkódot, ha fennhangon olvasod a while operátor kulcsmondatát (a végrehajtási szabályt). Például, ha a while operátorról van szó: Mindaddig amíg., hajtsd végre a következőt: Ebben az esetben, a while operátor fejléce a következő : amíg Delta nagyobb, mint D, hajtsd végre a következőt..., Ha a feltétel igaz, a program végrehajtja a ciklustörzsben lévő programsorokat majd újból elemzi a feltételt. A fibonacci.mq4 fenti példája szerint a vezérlést megkapta a while ciklusoperátor, a feltételben szereplő változók értékei és ; tehát a feltétel igaz a ciklusoperátornak szóló első hívásnál. Ez azt jelenti, hogy miután a feltételt teszteltük, a vezérlés át fog kerülni ciklus operátortörzsben lévő első operátorhoz. A példánkban ez a következő operátor: i++; // Counter A következő három sorban a sorozat újabb elemei lesznek kiszámolva: A = B; // Next value B = C; // Next value C = A + B; // Next value Könnyű belátni, hogy a változók felveszik a következő (legközelebbi nagyobb) elem értékét. Mielőtt a ciklusoperátort végrehajtjuk, A, B és C értéke egyenlő volt 1.0-val, 1.0-val és 2.0-val. Az első iterációnál (ismétlés) ezek a változók felveszik az 1.0, 2.0 és 3.0 értékeket. Az iteráció (ismétlés) néhány számítás ismételt végrehajtása; jelen esetben a ciklus-operátortörzset alkotó programsorok végrehajtása. A következő sorban kiszámítjuk a különbséget a Fibonacci számok között, a (C/B) és (B/A) hányadosra kapott eredmény alapján: Delta = MathAbs(C/B - B/A); // Search difference between coefficients Ebben az operátorban a beépített MathAbs() függvényt használjuk, amely kiszámítja a kifejezés abszolút értékét. Ahogy fent megemlítettük, a Fibonacci együtthatók közelítő értéke a számítások során a tényleges értékhez képest vagy nagyobb értéket vesz fel. Ezért a szomszédos együtthatók közti különbség lehet negatív vagy pozitív érték. Ugyanakkor csak az eltérés nagyságával és nem az irányával foglalkozunk (az abszolút érték). Így, bármilyen irányú a Fibonacci szám aktuális számított értékének az eltérése, a MathAbs(C/B B/A) vagyis a Delta változó értéke mindig pozitív

96 Ez az operátor az utolsó a ciklustörzset alkotó operátorok listájában. Ezt követi egy záró kapcsos zárójel a következő sorban. Miután az utolsó operátort a ciklustörzsben végrehajtottuk, a vezérlést megkapja a ciklusoperátor fejléce, hogy tesztelje a feltételt. Ez az a fő esemény, ami meghatározza a ciklusoperátor lényegét. A ciklusoperátor feltételének igaz vagy hamis volta alapján a vezérlést a következő iteráció (ismételt végrehajtás) vagy a ciklusoperátoron kívüli, következő operátor fogja kapni. Az első ismétléseknél kiderül, hogy a Delta változó számított értéke nagyobb, mint a D változó élőre beállított értéke. Vagyis ez azt jelenti, hogy a feltétel (Delta > D) igaz, tehát a vezérlést meg fogja kapni a ciklustörzs azért, hogy végrehajtsa a következő ismétlést (iterációt). Minden változó, ami szerepet játszott a számításokban, új értéket fog kapni: amint a ciklustörzs végrehajtása befejezödik a vezérlés megint átkerül a fejlécbe azért, hogy tesztelje a feltétel teljesülését. Ez az eljárás addig fog folytatódni, amíg ( while ) a ciklusoperátor feltétele hamissá válik. Amint Delta a változó értéke kisebb vagy egyenlő, mint D értéke a (Delta > D) feltétel nem igaz tovább. Ennek következtében a vezérlés átkerül a ciklusoperátoron kívüli következő a sorba: Alert("C=",C," Fibonacci number=",c/b," i=",i);//display on the screen Végül az Alert () ablakban a következő üzenet fog megjelenni: С= Fibonacci number=1.618 i=25 Ez azt jelenti, hogy a kívánt pontosságot a 25. ismétlésnél értük el, a Fibonacci sorozat elemének maximális értéke a számítások során volt és a Fibonacci együttható, ahogy vártuk Ez az üzenet jelenti a feladat megoldását. Itt jegyezzük meg, hogy az MQL4 a számításokat 15 számjegy pontossággal végezte. Ugyanakkor a Fibonacci együttható értékét csak 3 tizedes pontossággal látjuk. Ez abból fakad, hogy az Alert() függvény 4 tizedes jegyet használ és a tizedesek végén a nullákat nem mutatja. Ha a Fibonacci számot egy bizonyos, előre definiált pontossággal szeretnénk látni, meg kell változtatnunk a kódot: Alert("C=",C," Fibonacci number=",c/b* ," i=",i);// Message A következőt jól meg kell jegyezni: Elképzelhető, hogy a feltétel, amit a ciklus operátor fejlécben megadtunk mindig igaz marad. Ennek eredménye egy végtelen ciklus (looping) kialakulása. Looping (hurkolódás, végtelen ciklus) a ciklustest operátorainak folytonosan ismételt végrehajtása; ez egy olyan kritikus helyzet, ami egy hibás algoritmus végrehajtásából ered. Ha egyszer egy összehurkolódás kialakul, a program vég nélkül végrehajtja a ciklustörzs operátorainak blokkját. Lent egy egyszerű példa az összehurkolt while ciklusoperátorra: int i=1; // Formal parameter (counter) while (i > 0) // Cycle operator header i++; // Increment of the value of i A fenti példában, az i változó értéke növekszik (inkrementálódik) minden ismétlésnél. Ezért a feltétel soha sem fog hamissá válni. Hasonló a helyzet, ha semmit nem számolunk ki az operátor testben. Például, a lenti kódban: int i=1; // Formal parameter (counter) while (i > 0) // Cycle operator header Alert("i= ",i); // Display on the screen Az i változó értéke a ciklusban nem változik. A következő üzenet fog megjelenni a képernyőn mindegyik ismétlésnél: i=

97 Ez az eljárás vég nélkül fog ismétlődni. Ha egyszer egy végtelen ciklus kialakul, a vezérlés nem tud abból kilépni. Ez a helyzet különösen veszélyes a kereskedő Expert Advisorban és a kereskedő scriptekben. Ilyen esetben a környezeti változókat általában nem frissítik, mert a különleges függvény nem fejezi be a működésé, és a kereskedő nem érzékeli az összehurkolódást. Ennek az lesz az eredménye, hogy a program fölötti ellenőrzés megszűnik és a megbízások nem teljesülnek. A programozónak az olyan eseteket meg kell akadályoznia, amelyekben egy ellenőrizetlen összehurkolódás lehetősége fennáll. Nincs olyan technika ami programozható módon segítene felfedezni ezt a helyzetet sem a fordításnál, sem a végrehajtásánál. Az egyetlen lehetséges módszer az ilyen algoritmikus hibák észlelésére a kódok vizsgálata során a logikus érvelés és a józan ész

98 A for ciklusoperátor Egy másik ciklusoperátor a for' operátor. A ' for' operátor formátuma A 'for' ciklusoperátor teljes formátuma áll - a fejlécből, ami tartalmazza az 1.Kifejezést a Feltételt és a 2.Kifejezést - és áll a végrehajtható ciklustörzsből, amelyet kapcsos zárójelek közé zárunk. for (1.Kifejezés; Feltételt; 2.Kifejezés) // Ciklusoperátor fejléc // Nyitó kapcsos zárójel Operátor blokk.. // A ciklustörzs állhat.....alkotja a ciklustörzset //... több operátorból is // Záró kapcsos zárójel Ha a 'for' operátorban a ciklustörzs csak egy operátorból áll a zárójeleket elhagyhatjuk. for (1.Kifejezés; Feltételt; 2.Kifejezés) // Ciklusoperátor fejléc Egy operátor a ciklustörzs Az 1.Kifejezés a Feltétel és a 2.Kifejezés hiányozhatnak a kódból. Azonban az elválasztó karakternek "; (pontosvessző) maradnia kell a kódban. for ( ; Feltétel; 2.Kifejezés) // Nincs 1.Kifejezés // Nyitó kapcsos zárójel Operátor blokk.. // A ciklus test állhat.....alkotja a ciklus testet //... több operátorból is // Záró kapcsos zárójel // for (1.Kifejezés; ; 2.Kifejezés) // Nincs Feltétel // Nyitó kapcsos zárójel Operátor blokk.. // A ciklus test állhat.....alkotja a ciklus testet //... több operátorból is // Záró kapcsos zárójel // for ( ; ; ) // Nincsenek Kifejezések sem Feltétel // Nyitó kapcsos zárójel Operátor blokk.. // A ciklus test állhat.....alkotja a ciklus testet //... több operátorból is // Záró kapcsos zárójel A 'for' operátor végrehajtási szabálya Amint a vezérlést megkapja a for' operátor, a program végrehajtja az 1.Kifejezést. Mindaddig, amíg az operátor Feltétele (Condition) igaz: a vezérlést megkapja az első operátor a ciklus testben; amint minden operátort a ciklus testben végrehajtottak, a programnak végre kell hajtania a 2.Kifejezést és át kell adni a vezérlést a fejlécbe, hogy az tesztelhesse a feltételt. Ha a for' operátor feltétele hamis: a programnak át kell adnia a vezérlést annak az operátornak, amelyik követi a for' operátort

99 Gondolkozzunk, hogyan működik a 'for' ciklusoperátor. Olldjunk meg egy feladatot. 13. feladat: Nekünk van egy egész számsorunk: Alkossunk egy programot, ami kiszámítaná ezen sorozat elemeinek az összegét, N1-töl N2-ig. Ezt a problémát matematika szempontból könnyű megoldani. Tételezzük fel azt, hogy harmadik elemtől a hetedik elemig ki akarjuk számítani az elemek összegét. A megoldás ez lenne: = 25. Azonban, ez a megoldás csak egy speciális esetben jó, amikor az összeadandó elemek első és utolsó eleme 3 illetve 7. Egy programot, ami ezt a feladatot elvégzi úgy kell megalkotni, hogy a számsor bármely elemét választhassuk kezdő illetve végső értéknek (például 15-től 23-ig), és könnyen megváltoztathassuk a kezdő és végső értéket anélkül, hogy a programsorokat egyéb helyen módosítanánk. Lent van egy ilyen programváltozat (script sumtotal.mq4): // // sumtotal.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int Nom_1, // Number of the first element Nom_2, // Number of the second element Sum, // Sum of the numbers i; // Formal parameter (counter) // Nom_1=3; // Specify numeric value Nom_2=7; // Specify numeric value for(i=nom_1; i<=nom_2; i++) // Cycle operator header // Brace opening the cycle body Sum=Sum + i; // Sum is accumulated Alert("i=",i," Sum=",Sum); // Display on the screen // Brace closing the cycle body // Alert("After exiting the cycle, i=",i," Sum=",Sum);// Display on the screen return; // Exit start() // A program első soraiban a változókat deklarálunk (a megjegyzések megmagyarázzák a változók értelmét). Ezekben a sorokban: Nom_1 = 3; // Specify numeric value Nom_2 = 7; // Specify numeric value a tartomány első és utolsó elemének számszerű meghatározása történik. Vedd észre, hogy ezeket az értékeket sehol máshol nem kell megadni a programban. Ha szükséges, könnyen meg tudod változtatni ezeket az értékeket (egy helyen) anélkül, hogy a kód bármely másik sorát megváltoztatnád. Ezek után a vezérlést megkapja a 'for' ciklusoperátor: for(i=nom_1; i<=nom_2; i++) // Cycle operator header // Brace opening the cycle body Sum = Sum + i; // Sum is accumulated Alert("i=",i," Sum=",Sum); // Display on the screen // Brace closing the cycle body A kulcsmondat - a 'for' operátor végrehajtási szabálya - a következő: A kezdettől mindaddig lépj... hajtsd végre a következőt:. A lépj akkor használható, ha számlálót (például: i++ vagy i=i+2) alkalmazunk a 2.Kifejezésben. A példánkban a kulcsmondat a következő: Kezdetben i egyenlő Nom_1, mindaddig amíg i kevesebb vagy egyenlő Nom_2, lépj1-et, hajtsd végre a következőt: (a ciklustörzs végrehejtása)

100 A 'for' operátor végrehajtásszabálya szerint végre fogjuk hajtani ezt a blokkot a programból: 1. Az 1. Kifejezés végrehajtása: i=nom_1 Az i változó felveszi a Nom_1 változó számszerű értékét, vagyis 3 egész szám értéket. Az 1. Kifejezést csak egyszer hajtjuk végre - amikor a vezérlést megkapja a 'for' operátor. Az 1.Kifejezés nem vesz részt semmilyen rákövetkező eseményben. 2. A Feltételt teszteljük: i<=nom_2 A Nom_2 változó értéke az első ismétlésnél 7 (egész szám), míg az i változó egyenlő 3-mal. Ez azt jelenti, hogy a feltétel igaz (3 kevesebb 7-nél), vagyis a vezérlést megkapja a ciklustörzs, hogy azt végrehajtsa. 3. A példánkban a ciklustörzs két operátorból áll amelyeket egyenként végre fogunk hajtani: Sum = Sum + i; // Sum is accumulated Alert("i=",i," Sum=",Sum); // Display on the screen A Sum változó az inicializálásánál nem kapott kezdőértéket, ezért az értéke az első ciklus kezdete előtt egyenlő nullával. A számítások alatt a Sum változó értékét növeljük az i értékével, tehát az első ismétlés végén i egyenlő 3-mal. Ezt az értéket látjuk az Alert() függvény ablakában: i=3 Sum=3 4. Az utolsó esemény a ciklus operátor végrehajtása alatt a 2.Kifejezés végrehajtása: i++ Az i változó értéke eggyel növekszik. Ezzel az első ismétlésnek vége, a vezérlés teszteli a feltételt. Ezután a második ismétlés kezdődik a 2-4 lépésekkel. A számítások alatt a változók új értékeket kapnak. Esetünkben a második ismétlésnél az i változó értéke egyenlő 4-gyel, míg Sum egyenlő 7-tel. A megfelelő üzenet szintén meg fog jelenni. Általában véve, a program a lenti blokkdiagram szerint fog tovább haladni: 43. ábra: A 'for' operátor végrehajtásának a blokkdiagramja a sumtotal.mq4 programban. A program ciklikusan ismételni fogja a ciklusoperátor végrehajtását, amíg a Feltétel (Condition) hamissá válik. Ez akkor fog megtörténni, amikor az i változó értéke egyenlő lesz 8-cal, vagyis meghaladja Nom_

101 változó előre beállított értékét a 7-et. Ebben az esetben a vezérlés a 'for' operátoron kívülre kerül mégpedig ebbe a sorba: Alert("After exiting the cycle, i=",i," Sum=",Sum);// Display on the screen és végül a 'return' operátor végrehajtása befejezi a start() különleges függvény munkáját. Arra lehet használni ezt a programot, hogy bármilyen tartománynak az összegét kiszámítsa. Például, ha kicseréled a 3 és 7 állandókat 10-re és 15-re a program végrehajtása az ezen a tartományon belül értékek összegének a kiszámításával fog végződni. Néhány programnyelvben azok a változók, amiket a ciklusoperátor fejlécében használnak elvesztik az értékeiket miután kiléptünk a ciklusból. Ez nem így van az MQL4-ben. Minden változó, ami szerepet játszik bármilyen számításban, érvényes és megtartja az értékét miután kiléptünk a ciklusból. Ugyanakkor néhány sajátosság érinti azon változók értékeit, amik a számításokban szerepeltek. Jegyezd meg, hogy az utolsó két üzenet sor megmarad a számítások befejezése és a script eltávolítása után is: i=7 Sum=25 After exiting the cycle, i=8 Sum=25 A fentebb két üzenet közül az első még azelőtt megjelent, mielőtt az utolsó ciklus teljesen végrehajtódott, míg az utóbbit az Alert() függvény a program befejezése előtt küldte. Figyelmet érdemel, hogy ezekben a sorokban az i változó különböző értékei szerepelnek. A ciklus végrehajtása alatt ez az érték 7, de 8 lesz amikor kilépünk a ciklusból.hogy megértsük ez miért történik így, nézzük meg a 43. ábrát még egyszer! Az utolsó (ebben az esetben, az ötödik) ismétlés a feltétel tesztelésével kezdődik. Az i értéke ebben a pillanatban egyenlő 7-tel. Ez az érték nem nagyobb, mint a Nom_2 változó, a feltétel igaz és a ciklustörzset végre kell hajtani. Amint a ciklustörzset alkotó operátorok közül az utolsót is végrehajtjuk: a 2. kifejezést szintén végre kell hajtani. Ezt még egyszer hangsúlyozni kell: Az utolsó esemény, ami lejátszódik a 'for' operátor mindegyik ismétlésénél, a 2.Kifejezés kiszámítása. Ez ahhoz vezet, hogy az i változó értéke növekszik 1-el és 8-as értéket kap. A feltétel (Condition) következő tesztje (a következő ismétlés kezdetén) azt az eredményt adja, hogy a feltétel most hamis. Ez azt eredményezi, hogy a vezérlés a teszt elvégzése után a ciklusoperátoron kívül kerül. Ugyanakkor, az i változó értékel a ciklusoperátor elhagyásakor 8, míg a Sum változó értéke egyenlő 25-tel, mert a ciklustörzs operátorait már nem hajtjuk végre. Ezt figyelembe kell venni olyan helyzetekben, ahol az ismétlések (iterációk) számát előre nem ismerjük. Azt hihetnénk, hogy az olyan helyzet, ami hurkolódáshoz vezet kivételes. Ez különféle hibáknál könnyen lehetségessé válik. Például, ha hibásan használod a 2.Kifejezést: i-- mindegyik ismétlésnél a számláló értéke csökkenni fog, és a Feltétel soha nem válik hamissá. A másik hiba a helytelenül megfogalmazott Feltétel (Condition) lehet: for(i=nom_1; i>=nom_1; i++) // Cycle operator header Ebben az esetben, az i változó értéke mindig nagyobb vagy egyenlő lesz Nom_1 változó értékével, és a Feltétel mindig igaz lesz

102 A 'while' és 'for' ciklus operátorok felcserélhetők Hogy egyik vagy másik operátort használja a programozó döntésétől függ. Az egyetlen dolog, amit meg kell jegyezni, hogy ezeket az operátorokat szinonimákként használhatjuk; és a cseréjük csak egy kis finomítást jelent a kódban. Például: a 13. feladat megoldására a 'for' ciklusoperátort használtuk: for (i=nom_1; i<=nom_2; i++) // Cycle operator header // Brace opening the cycle body Sum = Sum + i; // Sum is accumulated Alert("i=",i," Sum=",Sum); // Display on the screen // Brace closing the cycle body Lent egy olyan példa látható, ahol ugyanazt a kódrészletet látjuk a 'while' operátor alkalmazásával: i=nom_1; // Оператор присваивания while (i<=nom_2) // Cycle operator header // Brace opening the cycle body Sum = Sum + i; // Sum is accumulated Alert("i=",i," Sum=",Sum); // Display on the screen i++; // Increment of the element number // Brace closing the cycle body Láthatjuk, hogy az 1.Kifejezés a ciklusoperátort megelőző sorba került, míg a 2.Kifejezés bekerült a ciklustörzsbe. Meg tudod változtatni a példa programot magad is. Indítsd el és futtasd, hogy lásd, az eredménye ugyanaz mindkét verziónak

103 A break operátor Néhány esetben, például a ciklusoperátorok programozása közben, szükség lehet a ciklus megszakítására mielőtt még a feltétel hamissá válna. Az ilyen problémákat megoldására használhatjuk a break operátort. A break operátor formátuma A break operátor csak egy szóból áll és végén a "; (pontosvessző) karakterből. break; // 'break' operátor A break operátor végrehajtási szabályai A break operátor leállítja a legközelebbi külső 'while', 'for' vagy 'switch' típusú operátor végrehajtását. A break operátor végrehajtása azon alapul, hogy átadja a vezérlést a 'while', 'for' vagy 'switch' operátoron kívül lévő következő legközelebbi operátornak. A break operátor csak a fent felsorolt operátorok munkáját képes megszakítani. Elképzelhetjük a 'break' operátor végrehajtását a következő példa alapján. 14. feladat: Legyen egy 1 méter hosszú fonalunk. A lehetséges maximális területű téglalap alakjában kell lefektetnünk ezt a fonalat. Meg kell határozni ennek a téglalapnak a területét az oldalak lehetséges hosszának 1 mm pontossággal történő sorozatos keresése által. Egy adott hosszúságú fonálból korlátlan mennyiségű, különböző méretű téglalap készülhet. Mivel a pontosság 1 mm adott, a variációk száma 499-re korlátozódik. Az első, legvékonyabb téglalap mérete 1х499 mm lesz a másodiké 2х498, stb. míg az utolsó téglalap mérete 499х1 mm lesz. Nekünk ezek közül a téglalapok közül mindegyiket meg kell vizsgálnunk és a legnagyobb területűt meg kell találnunk. Ahogy azonnal észrevehetjük, a téglalap dimenziók készletében ismétlődések vannak. Például az első és az utolsó téglalapnak ugyanazon dimenziói vannak: 1х499 mm (ugyanaz 499х1 mm). Hasonlóan a második téglalap dimenziói ugyanazok lesznek, mint az utolsó előtti téglalapnak. Nekünk csinálnunk kell egy olyan algoritmust, ami keresne minden variációban, de kihagyná az ismétléseket. Először is becsüljük meg a terület és az oldalak hossza közti kapcsolatot egy téglalapban. Könnyű belátni, hogy az első téglalap a 1x499 mm dimenzióival a legkisebb területű., Azután a rövidebb oldal növekedésével a téglalap területe növekedni fog. Amint elér egy bizonyos értéket, a téglalapok területei el fognak kezdeni csökkenni. Ez a kapcsolat látható a 44. ábrán: 44. ábra: A téglalap területe és az egyik oldala közötti összefüggés

104 A 44. ábrát szemlélve könnyen arra a következtetésre jutunk, hogy nekünk oly módon kell megtalálnunk a maximális területet, hogy az első variációtól kezdve addig fojtatjuk a keresést, amíg a terület a számítások alatt növekszik. Amint a terület elkezd csökkenni, meg fogjuk szakítani a keresést és ki fogunk lépni a keresőciklusból. Lent látható a rectangle.mq4 script, ami megvalósít egy ilyen algoritmust. // // rectangle.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int L=1000, // Specified thread length A, // First side of the rectangle B, // Second side of the rectangle S, // Area of the rectangle a,b,s; // Current values // for(a=1; a<l/2; a++) // Cycle operator header // Brace opening the cycle body b=(l/2) - a; // Current value of the sides s=a * b; // Current value of the area if (s<=s) // Choose the larger value break; // Exit the cycle A=a; // Save the best value B=b; // Save the best value S=s; // Save the best value // Brace closing the cycle body // Alert("The maximum area = ",S," A=",A," B=",B);// Message return; // Function exiting operator // Nézzük, hogyan működik ez a program. Változókat deklarálnak és megjegyzéseket tesznek a program kezdetén. Magát a feladatmegoldás algoritmusát a cikluson belül valósítják meg. A téglalap egyik oldalának a kezdőértéke az 1.Kifejezésben szerpel. A Feltétel alapján az értékeket tesztelik, addig amíg a téglalapoldal mérete a fonálhossz felénél kisebb marad. A 2.Kifejezés mindegyik ismétlésnél növeli a téglalap aktuális oldalhosszát. Az a, b és s változók azok az aktuális változók, amelyeknek az értékeit vizsgáljuk. A, B és S változók a keresett értékek. A b a télalap másikoldala és s a téglalap területe a ciklus kezdetén. b = (L/2) - a; // Current values of the sides s = a * b; // Current value of the area A ciklus elhagyásának a feltételét tesztelik az if' operátorban: if (s <= S ) // Choose the larger value break; // Exit the cycle Ha az újonnan kiszámított téglalap s területéről kiderül, hogy nagyobb, mint az előző ismétlésnél kiszámolt S terület, a jelenlegi s értéke a lehető legjobb eredménnyé válik. Ebben az esetben, az 'if' operátor feltétele nem teljesül, és a vezérlést átadják annak a legközelebbi operátornak, ami az 'if' operátort követi. Lent láthatók azok a sorok, ahol mentjük a legjobb eredményeket: A = a; // Save the best value B = b; // Save the best value S = s; // Save the best value Amint a program eléri a legközelebbi záró kapcsos zárójelet, az ismétlés kész és a vezérlés átkerül az operátor fejlécébe, ahol végrehajtja a 2.Kifejezést és teszteli a Feltételt. Ha az s oldal hossza még nem érte el az itt meghatározott értéket, a ciklus továbbra is végre kell hajtani

105 A ciklikusba számítások folytatódni fognak, amíg a következő események közül az egyik be nem következik: bármelyik oldal hossza felülmúlja az előre definiált értéket (a 'for' operátor feltétele szerint), vagy az s kiszámított terület mérete kisebbnek bizonyul egy korábban megtalált értéknél, amit az S változóban elraktároztunk. Jó okkal feltételezhetjük, hogy a ciklust az 'if' operátor feltételének teljesülése fogja megszakítani: if (s <= S ) // Choose the larger value break; // Exit the cycle Valóban, a ciklusoperátor olyan eljárást folytat melynek során kivétel nélkül megvizsgál minden lehetséges variációt, (L/2 a fonálhossz fele, a téglalap két oldalának az összege). A téglalap a maximális területét el fogja érni valahol a megvizsgált variáció készlet közepén. Amint ez megtörténik (az aktuális téglalap s területe kevesebb vagy egyenlő a korábban elért S értékkel), a vezérlést az if' operátor átadja a 'break' operátornak, ez utóbbi a vezérlést a 'for' operátoron kívülre helyezi a következő sorba: Alert("The maximum area = ",S," A=",A," B=",B);// Message Az Alert() beépített függvény végrehajtása következtében az alábbi sor jelenik meg a riasztási ablakban: The maximum area = А=250 В=250 Ezután a vezérlés a return' operátorhoz kerül, ami a start() különleges függvény végződéséhez vezet. Ennek következtében a script eltávolításra kerül az ügyfélterminál szimbólumablakából. Ebben a példában a break' operátor megszakította azt a 'for' ciklusoperátort, amelybe bele lett foglalva (kívül helyezte a vezérlést a ciklusoperátoron). Lent van egy for' ciklus blokkdiagramja egy különleges kijárattal: 45. ábra: Egy for' ciklus blokkdiagramja 'break' operátor használatával (rectangle.mq4). Ahogy a diagramból látjuk két cikluskijárat van: egy normális kijárat (a ciklus operátor fejlécében meghatározott feltétel szerinti kijárat) és egy különleges kijárat (a ciklustörzsben egy további feltétel, a break operátor szerinti kijárat). Nehéz túlbecsülni a azt a lehetőséget, hogy egy ciklusban különleges kijáratot használhatunk. A példánkban a break operátor lehetővé teszi, hogy egy olyan algoritmust csináljunk, ami a szükséges számításokat végrehajtja. Ez az algoritmus a különleges kijárat nélkül nem volna hatékony: ebben az esetben ismételt számításokat hajtana végre melyek ésszerűtlen időpocsékoláshoz és számítási erőforrások pazarlásához vezetnének. A 44. ábrán a sraffozott terület mutatja azon paramétereknek a régióját, amelyeket nem számoltunk ki a fenti programban (az összes számításnak majdnem a fele!), ennek ellenére megkaptuk a helyes eredményt. A hatásos algoritmusok használatának szükséglete nyilvánvalóvá válik, amikor a programvégrehajtás hosszú másodpercekig vagy percekig tart amely időtartamok felülmúlhatják a tickek közötti időintervallumot. Ebben az esetben meg van a kockázata annak, hogy kihagyjuk néhány fontos tick feldolgozását és végül elveszitjük a kereskedés irányítását. Különösen akkor tudod értékelni a számításokra fordított idő

106 csökkentését, amikor a programjaidat teszteled a Stratégiai Testerben. A tesztelés a bonyolultabb programok esetén hosszú órákig vagy napokig is eltart. A tesztelésre fordított idő, ebben az esetben egy nagyon fontos jellemző. A break operátor szabálya az, hogy leállítja a legközelebbi külső operátort. Lássuk, hogy egy program hogyan használ beillesztett ciklusokat! 15. feladat: a 14. feladat algoritmusa alapján keresd meg, hogy az 1 méter hosszú fonál hányszorosára van szükség egy 1.5 m² területű téglalap körülkerítéséhez. Ebben a problémában lineárisan kell keresni az elérhető fonálhosszakban és kiszámítani a maximális területet mindegyik fonálhossz számára. A 15. feladat megoldását az area.mq4. scriptben valósítjuk meg. A megoldás variációkat két ciklusban teszteljük: belsőben és külsőben. A külső ciklus 1000 mm lépenként keres a fonálhosszakban, míg a belső az aktuális hosszú fonállal megvalósítható maximális területet keresi. Ebben az esetben, a break operátort arra használjuk, hogy kilépjen mind a külső mind a belső ciklusból. // // area.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int L, // Thread length S_etalon= , // Predefined area (m²) S, // Area of the rectangle a,b,s; // Current side lengths and area // while(true) // External cycle for thread lengths // Start of the external cycle L=L+1000; // Current thread length of в mm // S=0; // Initial value.. //..for each dimension for(a=1; a<l/2; a++) // Cycle operator header // НStart of the internal cycle b=(l/2) - a; // Current side lengths s=a * b; // Current area if (s<=s) // Choose the larger value break; // Exit internal cycle S=s; // Store the best value // End of the internal cycle // if (S>=S_etalon) // Choose the larger value Alert("The thread length must be ",L1000," m.");// Message break; // Exit the external cycle // End of the external cycle // return; // Exit function operator // A belső ciklus itt ugyanúgy dolgozik, mint az előző probléma megoldásánál. A break operátort arra használjuk, hogy kilépjen a ciklusból, mikor a maximális területet az adott fonálhosszal megtaláltuk. Ennek a területnek ismertnek kell lennie amikor a break operátor, amit a belső ciklusban helyeztünk el leállítja a belső ciklust és a vezérlést átadja annak az operátornak ami a belső ciklust követi. Ez a jelenség sehogy sem befolyásolja a külső ciklusoperátor végrehajtását. Amint a 'break' operátor a belső 'for' ciklust megszakítja, a vezérlést az' if' operátornak adja:

107 if (S >= S_etalon) // Choose the larger value Alert("The thread length must be ",L1000," m.");// Message break; // Exit the external cycle Az if' operátorban azt ellenőrizzük, hogy a megtalált terület nagyobb vagy egyenlő, mint 1.5 m², ami a kérdésfelvetésnél meghatározott minimális érték. Ha igen, a megoldást meg találtuk és nincs értelme tovább számolni; a vezérlés átkerül az 'if 'operátor törzsébe. Az 'if' operátor végrehajtható része csak két operátorból áll, az első üzenetet jelenít meg a megoldásról a képernyőn: The thread length must be 5 m. A második operátort a break -et arra használjuk, hogy kilépjen a külső 'while ciklusból és azután kilépjen a programból. Az area.mq4 program algoritmusának blokkdiagramja lent látható: 46. ábra. Az (area.mq4) program blokkdiagramja, különleges kijárat a belső és külső ciklusokból. Ahogy a diagramból láthatjuk, mindkét ciklusnak van egy normális és egy különleges kijárata. Mindegyik break operátor leállítja a megfelelő ciklust, de ennek nincs hatása a másik ciklusra; mindegyik különleges kijáratot a ciklusoperátor saját break operátora nyitja. Ugyanazt a break operátort nem használhatjuk mindkét ciklus különleges kijáratának. Ebben az esetben mindegyik ciklusnak csak egy különleges kijárata van. Azonban lehetséges több 'break' operátor használatára, több különleges kijárat létrehozására egy ciklusból. Jegyezd meg, a Feltétel a külső 'while' ciklus fejlécében mindig igaz (true), vagyis ez egy szöveg, ami nem tartalmaz olyan változót, aminek az értéke a program végrehajtása alatt változna. Ez azt jelenti, hogy a program soha nem fog kilépni a ciklusból a fejlécben megadott Feltétel alapján. Ebben az esetben, az egyetlen lehetőség a ciklusból való kilépésre a break operátor használata. Ebben a példában az algoritmust úgy építettük fel, hogy a belső és a külső ciklusból is a kilépés a különleges kijáraton keresztül történik. Mindazonáltal nem kell a break operátor használatának mindig azzal végződnie, nem kell mindig olyan algoritmusokat építeni, amelyek egyedül a különleges kijáratokat használják. Más programok algoritmusában lehetséges, hogy a program eléri a fejlécben meghatározott feltételt és a normális kijáratot használja arra, hogy kilépjen a ciklusaiból. A break' operátor működésének teljes megismeréséhez meg kell vizsgálnunk a 'switch' operátor használatát is, amit a 'switch' operátor szekcióban ismertetünk

108 A 'continue' operátor Néha, egy ciklus alkalmazásakor szükség lehet az aktuális ismétlés (iteráció) idő előtti befejezésére, és a következő ismétlésre lépésre anélkül, hogy végrehajtanánk a ciklustestben fennmaradó operátorokat. Ilyen esetekben a continue operátort kell használnunk. A continue operátor formátuma Az continue operátor egy szóból és a végén "; -ből (pontosvessző) áll. continue; // 'continue' operátor A continue operátor végrehajtási szabályai A continue operátor leállítja a legközelebbi 'while' vagy 'for' ciklusoperátor aktuális ismétlésének a végrehajtását. A continue' operátor végrehajtása azt eredményezi, hogy a program a legközelebbi 'while' vagy 'for' ciklusoperátor következő ismétlésére lép. A continue operátort csak a fenti ciklusoperátorok testében tudjuk használni. Gondolkozzunk, a continue operátort hogyan tudnánk a gyakorlatban használni. 16. feladat: Az első farmon van 1000 juh. Ezen a farmon a juhok napi 1 százalékot szaporodnak. Ha a juhok száma a hónap végén meghaladja az et, akkor a juhok 10 százalékát át kell helyezni egy másik farmra. A második farmon a juhok száma mikor fogja a et elérni? (Legyen minden hónapban 30 nap.) A problémamegoldás algoritmusa nyilvánvaló: Szerveznünk kellene egy olyan ciklust, ahol a program az első farmon lévő juhok mennyiségét számolja. A problémafelvetés szerint a juhokat a hónap végén áthelyezik a második farmra. Ez azt jelenti, hogy nekünk létre kell hoznunk egy további (belső) ciklust, ahol az aktuális hónapban a juhok gyarapodását számoljuk. A hónap végén meg kell állapítanunk, hogy juhos határát átléptük-e, vagy sem. Ha igen, nekünk ki kell számolnunk, hogy valamennyi juhot fogunk áthelyezni a második farmra a hónap végén, és ezekkel a juhokkal a második farmon gazdálkodunk. A felvázolt algoritmust a sheep.mq4. scriptben valósítjuk meg. A continue operátor itt arra használjuk, hogy a külső ciklusban végezzen számításokat. // // sheep.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int day, // Current day of the month Mons; // Search amount of months double One_Farm =1000.0, // Sheep on the 1st farm Perc_day =1, // Daily increase, in % One_Farm_max= , // Sheep limit Perc_exit =10, // Monthly output, in % Purpose = , // Required amount on farm 2 Two_Farm; // Current amount of farm 2 //

109 ---- while(two_farm < Purpose) // External cycle on history // Start of the external cycle body // for(day=1; day<=30; day++) // Cycle for days of month One_Farm=One_Farm*(1+Perc_day/100);//Accumulation on the 1st farm // Mons++; // Count months if (One_Farm < One_Farm_max) // If the amount is below limit,. continue; //.. don't transfer the sheep Two_Farm=Two_Farm+One_Farm*Perc_exit/100;//Sheep on the 2nd farm One_Farm=One_Farm*(1-Perc_exit/100);// Remainder on the 1st farm // End of the external cycle body // Alert("The aim will be attained within ",Mons," months.");//display on the screen return; // Exit function start() // A program kezdetén, ahogy általában változókat írunk le és magyarázuk őket. Magát a számítást a 'while' ciklusban hajtjuk végre. Miután a számítást elvégeztük, a megfelelő üzenetet láthatjuk. A számítások a külső 'while' ciklusban addig folynak, amíg a célt el nem érjük, vagyis amíg a második farmon levő juhok száma el nem eléri a várt értékét. A belső ciklus nagyon egyszerű: az érték naponta 1 százalékkal növekszik. Az összeg elemzését nem hajtjuk végre ebben a ciklusban, mert a probléma felvetés szerint a juhokat csak a hónap végén tudjuk átvinni. Így a 'for' ciklus végén az One_Farm változó értéke egyenlő valamennyi juh számával az első farmon. Közvetlenül azután, a Mons változó kiszámolt értéke növekszik 1-el, a külső 'while' ciklus mindegyik ismétlésének a végrehajtásánál. Az juhok aktuális mennyiségétől függően az első farmon az alábbi két művelet közül az egyiket fogjuk végrehajtani: ha a juhok száma az első farmon meghaladja az határértékét, akkor az első farm juhainak 10 százalék át kell helyezni a második farmra; egyéb esetben a juhok maradnak az első farmon és tovább tenyésztik őket. Az algoritmust elágaztatjuk miközben az 'if' operátort használjuk: if (One_Farm < One_Farm_max) // If the amount is below limit,. continue; //.. don't transfer the sheep A kódból ezeket a sorokat következőképpen értelmezhetjük: Ha az első farmon levő juhok összege alatt van, akkor végrehajtjuk az operátort, az 'if' operátor törzsében. Az első ismétlésnél az első farmon levő juhok száma kevesebb az előre megadott értéknél, tehát a vezérlés átkerül az 'if' operátor testére, vagyis a 'continue' operátorra. A continue operátor végrehajtása a következőképpen zajlik: Befejezi a aktuális ismétlést a legközelebbi ciklusban (ebben az esetben ez a 'while' ciklusoperátor) és átadja a vezérlést a ciklusoperátor fejlécébe. Azokat a következő program sorokat, amelyek a 'while ciklusoperátor törzsét képezik nem fogjuk végrehajtani: Two_Farm = Two_Farm+One_Farm*Perc_exit/100;//Sheep on the 2nd farm One_Farm = One_Farm*(1-Perc_exit/100); // Remainder on the 1st farm Ezekben a fenti sorokban a második farmon lévő juh mennyiséget és az első farmon megmaradó juhokat számoltuk ki. Nem szabad ezeket a programsorokat az első ismétléseknél végrehajtani, mert az első farmon a juhok száma az es határt még nem érte el. Ez a continue operátor lényege, kihagyja ezeket a sorokat anélkül, hogy végrehajtanánk őket az aktuális ismétlésben. Az 'continue' operátor végrehajtásának eredménye az, hogy átadja a vezérlést a 'while' ciklusoperátor fejlécébe. Azután a feltételt a ciklus operátorban teszteljük és a következő ismétlés kezdődik. A ciklust ismét végrehajtjuk ugyanazon algoritmus szerint addig, amíg a juhok száma az első farmon el nem éri az előre definiált határt. A Mons értéke növekszik mindegyik ismétlésnél

110 A számolás egy bizonyos szakaszában az első farmon levő juhok száma meg fogja haladni az előre definiált es határt. Ebben az esetben, az if'' operátor végrehajtásánál a feltétel (One_Farm <One_Farm_max) hamissá válik és a vezérlés nem fog átkerülni az 'if' operátor törzsébe. Ez azt jelenti, hogy a continue operátort nem fogjuk végrehajtani, ezért a vezérlés át fog kerülni a 'while' ciklustörzs utolsó előtti sorába: Először a program úgy fog számolni, mintha a valamennyi juh a második farmon lenne, azután pedig kiszámolja az első gazdaságban maradó juhok számát. Miután ezeket a számításokat befejeztük a 'while' ciklus ismétlése kész lesz, és a vezérlés megint a ciklus fejlécbe kerül. Lent látható ennek az algoritmusnak a blokkdiagramja, amelyet a sheep.mq4 scriptben valósítottunk meg. 47. ábra. Egy program blokkdiagramja ahol a continue operátor megszakítja a külső ciklus ismétléseit (sheep.mq4). A diagramból láthatjuk, hogy az 'if' operátor feltételétől függően, a vezérlés azonnal átkerül a 'while ciklus fejlécébe (a continue operátor végrehajtásának eredményeképpen), vagy előbb néhány operátort még végrehajtonk, és csak azután kerül a vezérlés a 'while' ciklus fejlécébe. A ciklus végrehajtása egyik esetben sem ér véget. Jegyezd meg, a continue operátor jelenléte az operátor testben szükségképpen nem vezet az aktuális ismétlés végződéséhez. Ez csak akkor történik meg, ha ez végrehajtják, vagyis ha megkapja a vezérlést. A 'while' ciklusoperátor mindegyik ismétlésnél a Two_Farm változó új értéket kap. Amint ez az érték meghaladja az előre definiált határt (35 000), a feltétel a 'while' ciklusoperátorban hamissá fog válni, a számítások a 'while' ciklustörzsben nem fojtatódnak tovább és a vezérlés át fog kerülni ahhoz a legközelebbi operátorhoz, amelyik a ciklusoperátort követi: Alert("The aim will be attained within ",Mons," months.");//display on the screen Az Alert() függvény végrehajtása a következő üzenettel fog végződni: The aim will be attained within 17 months. A 'return' operátor befejezi a start() különleges függvény végrehajtását, ennek következtében a vezérlés átkerül az ügyfélterminálhoz, és ez a script végrehajtását befejezi, és azt el fogja távolítani a szimbólumablakból. A fenti algoritmus egy általános kijáratot nyit a 'while' ciklusból. Más esetekben a ciklusból történő kilépést a 'break' operátor végrehajtása is okozhatja (különleges kijárat). Azonban a ciklusból történő kilépés módja nem befolyásolja a continue operátor használatát

111 Az 'continue' operátor végrehajtás szabályában a legközelebbi operátor kifejezést használtuk. Ez nem azt jelenti, hogy a programsorok közel vannak egymáshoz. Nézzük meg a sheep.mq4. script kódját. Az 'for' operátor közelebb van a 'continue' operátorhoz, mint a 'while' ciklus fejléce. Azonban ez nem jelent semmit: A continue operátornak nincs kapcsolata a 'for' ciklussal, mert ez a 'for' ciklustörzsén kívül van. Általában egy program többszörösen egymásba ágyazott ciklusoperátorokat tartalmazhat. A continue operátor mindig egy másik operátor törzsében van. Ugyanakkor, a continue operátor a belső ciklusoperátor részeként természetesen a külső ciklusoperátor része is. A megállítja a legközelebbi ciklus operátor aktuális ismétlésének a végrehajtását kifejezés azt jelenti, hogy a continue operátor azt a legközelebbi ciklusoperátort befolyásolja aelyiknek a törzsén belül lokalizálják. Azért hogy ezt szemléltessük egy kissé változtassuk meg a problémafelvetést. 17. feladat: Az első farmon van 1000 juh. Ezen a farmon a juhok napi 1 százalékot szaporodnak. Azon a napon, amikor a juhok száma meghaladja az et a juhok 10 százalékát át kell helyezni egy másik farmra. A második farmon a juhok száma mikor fogja a et elérni? (Legyen minden hónapban 30 nap.) A 17. feladat megoldását az othersheep.mq4. scriptben valósítjuk meg. Ebben az esetben, az continue operátort használjuk a külső és belső ciklusokban is. // // othersheep.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // int day, // Current day of the month Mons; // Search amount of months double One_Farm =1000.0, // Sheep on the 1st farm Perc_day =1, // Daily increase, in % One_Farm_max= , // Sheep limit Perc_exit =10, // One-time output, in % Purpose = , // Required amount on farm 2 Two_Farm; // Current amount of farm 2 // while(two_farm < Purpose) // Until the aim is attained // Start of the external cycle body // for(day=1; day<=30 && Two_Farm < Purpose; day++)// Cycle by days One_Farm=One_Farm*(1+Perc_day/100);//Accumulated on farm 1 if (One_Farm < One_Farm_max) // If the amount is below limit,. continue; //.. don't transfer the sheep Two_Farm=Two_Farm+One_Farm*Perc_exit/100;//Accumulated on farm 2 One_Farm=One_Farm*(1-Perc_exit/100); //Remainder on farm 1 // if (Two_Farm>=Purpose) // If the aim is attained,.. continue; //.. don't count months Mons++; // Count months // End of external cycle body // Alert("The aim will be attained within ",Mons," months and ",day," days."); return; // Exit function start() // A feladat megoldásához naponta kell elemeznünk a juhok számát. Ezt a célt szolgálják a következő sorok: if (One_Farm < One_Farm_max) // If the amount is below limit,. continue; //.. don't transfer the sheep Two_Farm=Two_Farm+One_Farm*Perc_exit/100;//Accumulated on farm 2 One_Farm=One_Farm*(1-Perc_exit/100); //Remainder on farm

112 a belső ciklusban a hónap a napjait használjuk. Nyilvánvaló, hogy bizonyos mennyiségű juh átkerül a második farmra, ebben az esetben nem a hónap végén, hanem azon napon, amikor a juhok száma az első farmon éri el az előre definiált értéket. A continue operátor használatának oka itt ugyanaz: Ki akarjuk hagyni azokat a sorokat, amelyek olyan számításokat tartalmaznak, amelyekben juh átszállítás történik egy farmról a másikra, vagyis nem akarjuk végrehajtani ezeket a sorokat. Jegyezd meg, a 'while' ciklust nem szakítottuk meg, függetlenül attól, hogy a juhokat átvitték vagy nem, mert a 'continue' operátor csak a legközelebbi ciklusoperátor van hatással és ez az esetünkben a 'for' ciklusoperátor. Feltételként egy összetett kifejezést használtunk a 'for' ciklusoperátorban: for(day=1; day<=30 && Two_Farm<Purpose; day++)// Cycle by days Az && (és) műveletet használjuk, ami azt biztosítja, hogy a ciklust addig fogjuk végrehajtani, amíg mindkét feltétel igaz: -a hónap nem ért még véget, vagyis a day változó az 1-30 tartományon belül van és -a második farmon levő juhok összege még nem érte el az előre definiált értéket, a Two_Farm változó kevesebb Purpose-nál (35 000). Ha a feltételek közül legalább az egyik nem teljesül (hamissá válik), a ciklus megáll. Ha a ciklus végrehajtása leáll (bármilyen okból), a vezérlést átadjuk az 'if' operátornak: if (Two_Farm >= Purpose) // If the aim is attained,.. continue; //.. don't count months Mons++; // Count months A continue operátor használatának ebben a kódban egyszerű oka van: A programnak továbbra is csak hónapokat kell számolnia,akkor ha a juhok száma a második farmon a várt érték alatt van. Ha a célt már elértük, a Mons változó értéke nem fog változni, és a vezérlés (a continue végrehajtásnak eredményeképpen) át fog kerülni a legközelebbi ciklusoperátor fejlécébe, ami a 'while' ciklusoperátor. Az continue operátorok használatát egymásba ágyazott ciklusokban láthatjuk a 48. ábrán. 48. ábra. Azon algoritmus blokkdiagramja, amely egymásba ágyazott ciklusokon alapult. Mindegyik ciklustestörzs tartalmazza a 'continue' operátort (othersheep.mq4). A fenti példában mindegyik ciklusban (külsőben és belsőben) ott van egy-egy continue' operátor, de azok csak a saját legközelebbi ciklusukat befolyásolják, és semmilyen hatással nincsenek a másik ciklusra. Általában egy ciklustörzsben több continue operátor használhatunk, mindegyik egy feltétel teljesülésének eredményeképpen félbeszakítja az aktuális ismétlést. A continue operátorok száma egy ciklustesten belül nem korlátozott

113 A switch operátor Néhány program algoritmusa több elágazást tartalmaz különféle variációban. Ilyen esetben nagyon kényelmes a 'switch operátort használni különösen ott ahol több tíz vagy több száz a variációk száma, mert az if operátor kódja nagyon terjedelmessé válik, ha sok egymásba ágyazott operátort használunk. A 'switch' operátor formátuma A 'switch' operátor a fejlécből és a végrehajtható testből áll. A fejléc tartalmazza az operátor nevét és egy zárójelbe zárt kifejezést. Az operátortörzs egy vagy több 'case' variációt tartalmaz, valamint az alapértelmezett default' variációt. Mindegyik 'case' variáció a 'case' kulcsszóból, egy állandóból, egy : (kettőspontból), és operátorokból áll. A 'case' variációk száma nem korlátozott. A default' (alapértelmezett) variáció a 'default' kulcsszóból, : (kettőspontból), és operátorokból áll. Általában a 'default' variációt a 'switch' operátor törzsében utolsóként helyezzük el, de azt bárhol lokalizálhatjuk az operátortörzsön belül, vagy akár hiányozhat is. switch ( Kifejezés ) // Operátor header // Nytó kapcsoszárójel case Paraméter: Operátorok // Egy 'case' variáció case Paraméter: Operátorok // Egy 'case' variáció... [default: Operators] // Záró kapcsoszárójel A Kifejezés és a parameterek értékei csak in típusú értékek lehetnek. A Kifejezés egy állandó, egy változó, egy függvényhívás vagy egy kifejezés lehet. A 'case' variációk lehetnek: egész számállandók, karakterállandók vagy állandókat tartalmazó kifejezések. Az állandó kifejezés nem tartalmazhat változókat vagy függvényhívásokat. A switch operátor használatának szabályai A programnak, abban az esetben, ha valamely 'case' variációban a Paraméter értéke ugyanaz, mint a fejlécben lévő kifejezés értéke, át kell adnia a vezérlést annak az első operátornak, ami a 'case' variáció : -ja (kettőspont) után következik, majd azután egyenként végre kell hajtania minden operátort, amely a 'switch' operátortörzsben található. A Kifejezés és a Paraméterek közötti egyenlőség feltételét fentről és balról jobbra teszteli a program. A Paraméterek értékeinek a különböző 'case' variációkban nem szabad azonosnak lenniük. A 'break' operátor megszakíthatja a 'switch' operátor végrehajtását és átadhatja a vezérlést annak az operátornak, amely a 'switch' operátort követi. Könnyű észrevenni, azt hogy a 'case Paraméter:' csak egy olyan címke, ami a vezérlés átvételét jelenti. A 'switch' operátor testét alkotó operátorok végrehajtása ettől a címkétől kezdődik. Ha a programalgoritmus szerint az összes variációból csak egy 'case' variációban leírt operátorcsoport végrehajtására van szükség, akkor az operátorcsoportokban az operátorok felsorolását a 'break' operátorral kell befejezni. A switch' operátor használatára lássunk néhány példát! A példák a 'switch' operátorokra 18. feladat: Alkoss egy olyan programot, ahol a következő feltételeket kell teljesíteni: Ha az ár meghaladja az előre definiált szintet, a programnak tájékoztatnia kell erről a kereskedőt, egy szöveges üzenetben, ahol a szint túllépést szavakba jelenítjük meg (10 pontig); egyéb esetben a programnak

114 tájékoztatnia kell arról, hogy az ár nem haladta meg előre definiált szintet. Lent látható a probléma megoldása a 'switch' operátor alkalmazásával (Expert Advisor pricealert.mq4): // // pricealert.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' double Level=1.3200; // Preset price level int Delta=NormalizeDouble((Bid-Level)Point,0); // Excess if (Delta<=0) // Price doesn't excess the level Alert("The price is below the level"); // Message return; // Exit start() // switch(delta) // Header of the 'switch' // Start of the 'switch' body case 1 : Alert("Plus one point"); break;// Variations.. case 2 : Alert("Plus two points"); break; case 3 : Alert("Plus three points"); break; case 4 : Alert("Plus four points"); break;//here are presented case 5 : Alert("Plus five points"); break;//10 variations 'case', case 6 : Alert("Plus six points"); break;//but, in general case, case 7 : Alert("Plus seven points"); break;//the amount of variations 'case' case 8 : Alert("Plus eight points"); break;//is unlimited case 9 : Alert("Plus nine points"); break; case 10: Alert("Plus ten points"); break; default: Alert("More than ten points"); // It is not the same as the 'case' // End of the 'switch' body // return; // Exit start() // Ebben a problémamegoldásban a 'switch' operátort használjuk, ahol mindegyik case' variációban break' operátort alkalmazunk. A Delta változó értékétől függően a vezérlés az egyik 'case' variációhoz kerül. Ennek következtében, az ebben a variációban lévő operátorokat végrehajtják: Alert() függvény és 'break' operátor. A 'break' operátor leállítja a 'switch' operátor végrehajtását, vagyis a vezérlést ezen kívül helyezi a return' operátornak, amely befejezi a start() függvény. Így a Delta változó értékétől függően a 'case' variációk egy részét feldolgozzuk, míg a másik részük feldolgozatlan marad. A fenti program egy Expert Advisor, vagyis az el fog indulni minden ticknél és ezért mindig az aktuális helyzetnek megfelelő üzenetet küldi. Természetesen nekünk esetleg módosítanunk kell a Level értékét annak a szimbólumnak a jelenlegi árához közelebbi értékre, amely szimbólum ablakához csatoljuk ezt az EA-t

115 49. ábra. A 'switch' operátor blokkdiagramja pricealert.mq4 EA-ban. A fenti blokkdiagramban világosan láthatjuk azt, hogy a 'break' operátor jelenléte miatt ki tudunk lépni mindegyik 'case' variációból az ebben a variációban lévő operátorok végrehajtása után a 'switch' operátoron kívülre. Ennek az algoritmusszerkezetnek hasonló elve, a 'switch' operátor használatának tekintetében, mint az stdlib.mq4 fájnak, aminek az elérési útja:(..\experts\libraries\stdlib.mq4). Találjunk egy másikfeladatot ahol nincs mindegyik case' variációban 'break operátor. 19. feladat: Van 10 bár. Meg kell jeleníteni a bárok számát n-től kezdődően. Meglehetősen könnyű kódolni ennek a feladatnak a megoldását (barnumber.mq4 script ): // // barnumber.mq4 // The code should be used for educational purpose only. // int start() // Special function start() int n = 3; // Preset number Alert("Bar numbers starting from ", n,":");// It does not depend on n // switch (n) // Header of the operator 'switch' // Start of the 'switch' body case 1 : Alert("Bar 1"); // Variations.. case 2 : Alert("Bar 2"); case 3 : Alert("Bar 3"); case 4 : Alert("Bar 4"); // Here are 10 variations.. case 5 : Alert("Bar 5"); //..'case' presented, but, in general,.. case 6 : Alert("Bar 6"); //..the amount of variations.. case 7 : Alert("Bar 7"); //..'case' is unlimited case 8 : Alert("Bar 8"); case 9 : Alert("Bar 9"); case 10: Alert("Bar 10");break; default: Alert("Wrong number entered");// It is not the same as the 'case' // End of the 'switch' body // return; // Operator to exit start() // A 'switch' operátorban, a program addig fog keresni a 'case' variációk között, amíg azt nem észleli, hogy a Paraméter egyenlő a fejlécben lévő Kifejezéssel. Mikor a Kifejezés értéke (ez esetben ez 3) egyenlő

116 valamely Paraméterrel (itt van ilyen ez 3), minden operátort, ami az adott variációban (case 3:) a kettőspontot követ végre fogunk hajtani. Az Alert(" Bar 3 ), függvényhívást, aztán a következőket Alert("Bar 4 ), Alert("Bar 5 ), stb. addig, amíg a 'break' operátorhoz nem érünk, amely befejezi a 'switch' operátor működését. Ha a Kifejezés értéke nem esik egybe semelyik Paraméter értékével sem, a vezérlést megkapja az az operátor(csoport), amelyik a 'default' variációhoz tartozik: 50. ábra: A 'switch' operátor blokkdiagramja a barnumber.mq4 scriptben. Az előző programban megvalósított algoritmustól eltérően, ebben az esetben (50. ábra), nem használjuk a 'break' operátort mindegyik 'case' variációba. Ezért, ha az Kifejezés értéke egyenlő valamely Paraméter értékével minden operátort végre fog hajtódni, ami az egyezés utáni 'case' variációkhoz tartozik. Egy fontos feladata van az utolsó 'case' variációhoz tartozó 'break operátornak: megakadályozni a 'default 'variációhoz tartozó operátorok végrehajtását. Ha nincs egyezés az Kifejezés és a Paraméterek értékei között, a vezérlést a 'default' variációhoz tartozó operátorok fogják megkapni. Ha az előre beállított n változó értéke az 1-től 10-ig tartó érték tartományon belül van, minden bár száma megjelenítésre kerül n-től kezdődően. Ha az n értéke a fenti tartományon kívül van, a program nem fog üzenetet küldeni a felhasználónak. Jegyezd meg: Nem szükséges a 'case' variációkat a Kifejezések értéke szerint rendezni a programodban. A case' variációk Kifrjezéseinek a feltételei a programod algoritmusának megfelelő sorrendben kövessék egymást

117 Függvényhívás Egy függvényhívást egy különálló operátorként használhatunk, és bárhol lehet a programban ahol egy bizonyos értékre utal (az előre definiált esetek kivételével). A függvényhívás formátum és a végrehajtás szabályai a standard (beépített) és felhasználói függvények esetén is azonos. A függvényhívás formátum Egy függvényhívás a függvény névből és az átadott paraméterek listájából áll, amit zárójelek közé zárunk: Function_name (Paraméterek); // Egy függvényhívás Annak a függvénynévnek amit a függvényhívásban megadunk ugyanannak kell lennie, mint annak a függvénynek a neve, amit végrehajtásra hívunk. A paramétereket a listában vesszőkkel választjuk el. Az átadott paraméterek száma korlátozott és nem lehet több 64-nél. Egy függvényhívásban paraméterként lehet használni állandókat, változókat és másik függvényhívásokat. A függvényhívásban levő átadott paraméterek számának, típusainak és sorrendjének ugyanannak kell lenniük, mint a formális paraméterek száma, típusai és sorrendje, amelyeket a függvényleírásban megadtak, (kivétel ha a függvényhívás a függvény alapértelmezett paramétereivel történik). My_function (Alf, Bet) // Példa a függvényhívásra // Ahol: My_function // A hívott függvény neve Alf // Az első átadott paraméter Bet // A második átadott paraméter Ha a hívott függvény nem igényel átadott paramétereket, a paraméterek listája üres, de a zárójeleket ki kell tenni. My_function () // Példa a függvényhívásra // Ahol: My_function // A hívott függvény neve () // Nincs átadott paraméter Ha a programnak egy függvényt az alapértelmezett paraméterekkel kell hívnia, az átadott paraméterek listája hiányos lehet (megrövidített). Ha nem adunk új érték a függvény minden alapértelmezett paraméterének, akkor az átadott paraméterek társítását az első alapértelmezett paraméterrel kezdjük. A lenti példa az a b, c, és d lokális változók néhány értékét mutatja: // A függvény leírása a következő: int My_function (int a, bool b=true, int c=1, double d=0.5) Operátorok //.. a következő függvényhívások lehetségesek: My_function (Alf, Bet, Ham, Del) // Lehetséges függvényhívás My_function (Alf ) // Lehetséges függvényhívás My_function (3) // Lehetséges függvényhívás My_function (Alf, 0) // Lehetséges függvényhívás My_function (3, Tet) // Lehetséges függvényhívás My_function (17, Bet, 3) // Lehetséges függvényhívás My_function (17, Bet, 3, 0.5) // Lehetséges függvényhívás Ha egy paraméternek nincs alapértelmezett értéke akkor annak megadását a függvényhívásbanl nem hagyhatjuk el. Ha egy alapértelmezett paraméter kimarad, a következő alapértelmezett paramétereket sem lehet megadni. // A függvény leírása a következő: int My_function (int a, bool b=true, int c=1, double d=0.5) Operátorok

118 //..a következő függvényhívások hibásak: My_function () //Hibás függvényhívás: a nem alapértelmezett.. //..paraméterek nem hagyhatók el(a legelső) My_function (17, Bet,, 0.5) // Hibás függvényhívás: kimaradt egy.. //..alapértelmezett paraméter (a harmadik, majd a következő új értéket kap) A hívott függvények két csoportba oszthatók: azokra, amelyek visszaküldik egy előre definiált típus egy bizonyos értékét és azokra, amelyek nem küldenek vissza értéket. A visszatérési értéket nem szolgáltató függvény hívásának formátuma Egy olyan függvénynek szóló hívást, amely nem küld vissza értéket csak különálló operátorként hozhatunk létre. A függvényhívás operátort a "; (pontosvessző) fejezi be: Function_name (Paraméter_lista); // Egy olyan függvénynek szóló hívás, amely nem küld vissza értéket egy operátorként létrehozva Func_no_ret (Alpha, Beta, Gamma); // Példa egy olyan függvényhívás operátorra.. //.. ahol a hívott függvény nem küld vissza értéket Nincs másik módszer (technika) olyan függvények hívására, amelyek nem küldenek vissza semmilyen értékeket. A visszatérési értéket szolgáltató függvények hívásának formátuma Egy olyan függvénynek szóló hívást, ami egy értéket küld vissza létrehozhatunk különálló operátorként, vagy azt használhatjuk a programkódban olyan helyeken, ahol egy bizonyos típusú értékre utalunk. Ha a függvényhívást különálló operátorként alkalmazunk, akkor az pontosvesszővel végződik: Function_name (Paraméter_lista); // Értéket visszaküldő függvény hívása mint operátor Func_yes_ret (Alpha, Beta, Delta); //Példa egy olyan függvény hívására.. //.. amely függvény értéket küld vissza A függvényhívás végrehajtási szabályai Egy függvényhívás neve azonos annak a függvénynek a nevével, amelyet végrehajtásra hív. Ha a függvényhívást különálló operátorként alkalmazzuk, miután a függvény végrehajtódott, a vezérlést átadják annak az operátornak, amely követi a függvényhívást. Ha a függvényhívást egy kifejezésben használjuk, miután a függvény végrehajtódott a vezérlés visszakerül abba a kifejezésbe, ahonnan a függvényhívást elindítottuk; a további számításokat ebben a kifejezésben a függvény visszatérési értékének használatával fogjuk elvégezni. Másik operátorokban levő függvényhívások használatát meghatározza ezeknek az operátoroknak a formátuma. 20 feladat: Alkoss egy olyan programot, ahol a következő feltételeket teljesítjük: - ha az aktuális idő több mint 15:00, 10 ismétlést hajtunk végre a ciklusban; - minden más esetben 6 ismétlést hajtunk végre. A lenti példa, a callfunction.mq4 script a következőket tartalmaz. Egy függvényhívást az operátor fejlécében (1.Kifejezés a 'for' operátor formátuma szerint, lásd: 'for' ciklusoperátor), egy beépített

119 függvény hívását különálló operátorként, az értékadó operátor jobb oldalán (lásd: Értékadó operátor), és egy függvényhívást az 'if-else' operátor fejlécében (a Feltétel az 'if-else' operátor formátuma szerint, lásd: 'if-else' feltételes operátor). /// // callfunction.mq4 // The code should be used for educational purpose only. // int start() // Description of function start() // Start of the function start() body int n; // Variable declaration int T=15; // Predefined time for(int i=func_yes_ret(t);i<=10;i++) // The use of the function in.. //.the cycle operator header // Start of the cycle 'for' body n=n+1; // Iterations counter Alert ("Iteration n=",n," i=",i); // Function call operator // End of the cycle 'for' body return; // Exit function start() // End of the function start() body // int Func_yes_ret (int Times_in) // Description of the user-defined function // Start of the user-defined function body datetime T_cur=TimeCurrent(); // The use of the function in.. //..the assignment operator if(timehour(t_cur) > Times_in) // The use of the function in.. //..the header of the operator 'if-else' return(1); // Return value 1 return(5); // Return value 5 // End of the user-defined function body // A fenti példában a lévő függvényeket a következő átadott paraméterekkel hívtuk: a Func_yes_ret(T) függvény hívása - T változó; az Alert () függvény hívása - Iteration n= és i= string állandók, valamint n és i változók; a TimeCurrent() függvény hívása nem ad át paramétert; a TimeHour(T_cur) függvénynek szóló hívás - a T_cur változót adja át paraméterként. Egy nagyon egyszerű algoritmust valósítunk meg ebben a programban. A számításaink alapja a T változó az idő (órákban). A 'for' operátor fejlécében, helyeztük el a Func_yes_ret() felhasználói függvénynek szóló hívást, amely két érték valamelyikét küldheti vissza: 1 vagy 5. a visszaküldött érték szerint a ciklusban levő ismétlések száma változni fog: lehet 10 (1-től 10-ig) vagy 6 (5-től 10-ig). Jobb megjelenítés érdekében a ciklustestben használjuk a számlálót, melynek mindegyik értékét kijelzi a képernyőn az Alert() függvényt. A felhasználói függvény leírásában először másodpercekben számoljuk az január 1-je 00:00, óta eltelt időt (TimeCurrent() függvény hívása), azután órákban kiszámítjuk az aktuális időt (TimeHour() függvény hívása). Az algoritmust elágaztatjuk az 'if' operátorral (a TimeHour() függvényhívás a feltételében szerepel). Ha az aktuális idő (Times_in helyi változó) több, mint amit paraméterként (T) elküldtünk a felhasználói függvénybe, a függvény visszatérési értéke1, máskülönben a visszatérési érték 5. Kérlek jegyezd meg: A standard (beépített) függvényeknek nincs leírása és a start() függvénynek nincs függvényhívása a programban. Lent láthatod a callfunction.mq4 script blokkdiagramját:

120 51. ábra. A program blokkdiagramja függvényhívások használatával. A körök a diagramban függvényhívásokat jelölnek (a standard és felhasználói függvények). A vörös nyilak a vezérlés átadást mutatják a függvényben oda és vissza. Világosan látható, hogy a függvény visszaküldi a vezérlést oda, ahonnan a függvényhívás érkezett, nincsenek számítások a függvényhívás és a függvény közötti útvonalon. Általában, ha egy függvény visszaküld egy értéket, ezt az értéket a hívó modul kapja (a piros nyíl mentén a függvényhívás irányában). Különleges függvényeket ( init(), start(), deinit()), az általános szabály szerint a programban bárhonnan hívhatunk, e tekintetben a többi függvénnyel egyenlők. A különleges függvényeknek szintén lehetnek paramétereik. Mindazonáltal, amikor az ügyfélterminál hívja ezeket a függvényeket, paramétereket nekik nem fog kívülről küldeni, az alapértelmezett értékeket fogja használni. A különleges függvényekben levő paraméterek használata csak akkor lesz ésszerű, ha őket egy programból hívjuk. Bár technikailag lehetséges, hogy a különleges függvényeket hívjunk egy programból, ez nem ajánlott. Meg kell fontolni egy programból a különleges függvényhívások használatát: ez helytelen

121 Függvényleírás és 'return' operátor Ha azalapján csoportosítjuk a függvényeket, hogy kell-e a programban függvényleírás, akkor 2 csoportba oszthatjuk őket: Olyan függvényekre, amelyeket nem írnak le egy programban, és olyan függvényekre, amelyeket le kell írni a programban. Standard (beépítetett) függvényeket nem írnak le a programokban. A felhasználói függvényeket le kell leírni minden programban. A különleges függvényeket, ha van ilyen, szintén le kell írni a programban. A függvényleírás formátuma Egy függvényleírás két alapvető részből áll: függvényfejléc és függvénytörzs. A függvényfejléc tartalmazza a visszaadott érték fajtáját (típusát), a függvény nevét, és a formális paraméterek listáját, amelyeket zárójelbe teszünk. Ha egy függvénynek nem szabad visszaküldenie értéket, a típusának a neve void. A függvénytörzs egyszerű és/vagy összetett operátorokból és másik függvényeknek szóló hívásokból állhat, és kapcsos zárójelbe tesszük. A visszatérési érték típusa Függvény_név (A formál paraméterek listája)//fejléc // Nyitó kapcsos zárójel A program kód // A függvénytörzs operátorokból.. alkotja a //.. és más függvényeknek szóló.. függvény törzset //.. függvényhívásokból állhat // Záró kapcsos zárójel A paramétereket a listában vesszők választják el. A függvény paraméterek száma korlátozott és nem lehet több 64-nél. Formális paraméterekként a függvényfejlécben csak változókat szabad használni (nem lehetnek állandók, másik függvényhívások vagy kifejezések). Az függvényhívásban levő átadott paraméterek számának, fajtájának és sorrendjének ugyanannak kell lennie, mint a függvényleírásban részletezett formális paraméterek száma fajtája és sorrendje (az egyetlen kivétel az olyan függvénynek szóló hívás, amely függvénynek alapértelmezett paraméterei vannak): int My_function (int a, double b) // Példa a függvény leírásra int c = a * b + 3; // Függvénytörzs operátor return (c); // Függvény visszatérési operátor (kilépés a függvényből) // Ahol (a fejléc balról jobbra): int // A visszatérési érték típusa My_function // A függvény neve int a // Az első formál paraméter 'a' típusa: int double b // A második formál paraméter 'b' típusa: double A függvényekben a paramétereknek lehetnek olyan alapértelmezett értékeik, amelyeket a megfelelő típusú állandóhatároz meg: int My_function (int a, bool b=true, int c=1, double d=0.5)//példa a függvény leírásra a = a + b*c + d2; // Függvénytörzs operátor int k = a * 3; // Függvénytörzs operátor return (k); // Függvény visszatérési operátor // Ahol (a fejléc balról jobbra): int // A visszatérési érték típusa My_function // A függvény neve int a // Az első formál paraméter 'a' típusa: int (alapértelmezett értéke nincs) bool b // A második formál paraméter 'b' típusa: double true // A 'b' formál paraméter alapértelmezett értéke egy állandó int c // A harmadik formál paraméter 'c' típusa: int

122 1 // A 'c' formál paraméter alapértelmezett értéke egy állandó double d // A negyedik formál paraméter 'd' típusa: double 0.5 // A 'd' formál paraméter alapértelmezett értéke egy állandó a,b,c,d,k // Helyi változók (hatókörük csak ez a függvény) Ha paraméterekkel rendelkező függvényhívással hívunk egy alapértelmezett értékekkel rendelkező függvényt, akkor a hívásban szereplő aktuális paraméterekkel lesz a függvény végrehajtva. Ha nincsenek aktuális paraméterek a függvényhívásban, akkor az alapértelmezett értékekkel rendelkező függvények az alapértelmezett értékekkel lesznek végrehajtva. A különleges függvényeknek szintén lehetnek paramétereik. Mindazonáltal, az ügyfélterminál nem ad semmilyen paramétert kívülről, amikor hívja ezeket a függvényeket, éppen ezért ezek az alapértelmezett értékeket használják. A különleges függvényeket szintén bárhonnan hívhatjuk, az általános szabály szerint ezek a többi függvényekkel egyenrangúak. A függvényvégrehajtás szabályai A függvényleírás helye egy programban: A függvényleírást bármilyen másik függvényen kívül, külön kell elhelyezni a programodban (nem szabad egy másik függvényen belül elhelyezni). Függvényvégrehajtás: Egy függvényhívás végrehajtása annak a kódnak a végrehajtását jelenti, amely kód a függvény törzsét alkotja. A 'return' operátor formátuma A függvény visszatérési értéke annak a paraméternek az értéke, amit a 'return' operátor zárójeleiben, megadtunk. A 'return operátor a 'return' kulcsszóból és a zárójelbe tett kifejezésből (Expression) áll, és pontosvessző karakterben végződik. A 'return' operátor teljes formátuma: return (Expression); // Operator return A zárójelben egy kifejezés egy állandó, egy változó vagy egy függvényhívás is lehet. Az visszatérési érték fajtájának a 'return' operátor használta során ugyanannak kell lennie, mint amit megadtak a függvényfejlécben. Ha ez nem így van, a 'return' operátorban visszaküldött kifejezés típusát módosítani kell (typecasting), a függvényleírás fejlécében megadott típusra. Ha a typecasting lehetetlen a Meta Editor egy hibaüzenetet fog adni a program fordítása során. A 'return' operátor végrehajtásának szabályai A 'return' operátor leállítja a legközelebbi külső függvény végrehajtását és átadja a vezérlést a hívó programrésznek azok szerint a szabályok szerint, amiket meghatároztunk a függvényhíváskor. A függvény visszatérési értéke annak a kifejezésnek az értéke, amit megadtunk a return' operátorban. Ha a paraméterérték típusa, amit megadtunk a 'return' operátorban, más, mint függvényfejlécben megadott típus, akkor azt módosítani kell a függvényfejlécben megadott visszatérési érték típusra. Egy példa arra, hogy hogyan lehet használni a 'return' operátort:

123 bool My_function (int Alpha) // Egy felhasználói függvény leírása // A függvénytörzs kezdete if(alpha>0) // 'if' operátor // Az 'if' operátortörzs kezdete Alert("The value is positive"); // Beépített függvény hívása return (true); // Az első kijárat a felhasználói függvényből // Az 'if' operátortörzs vége return (false); // A második kijárat a felhasználói függvényből // A függvénytörzs vége Ha egy függvény visszaadott értéke void típusú, a 'return operátort kifejezés nélkül kell használni: return; // Operator 'return' without the expressions in parentheses Egy példa a 'return' operátor visszaadott érték nélküli használatára: void My_function (double Price_Sell) // Description of the userdefined function // Start of the function body if(price_sell-ask >100 * Point) // Operator 'if' Alert("Profit for this order exceeds 100 п");// Standard function call return; // Exit function // End of the function body Lehetséges, hogy egy függvényleírás nem tartalmazza a 'return' operátort. Ebben az esetben a függvény automatikusan be fog fejeződni, amint (az algoritmus szerint) az utolsó operátort is végrehajtják a függvénytestben. Egy példa a 'return' operátor nélküli függvényleírására: void My_function (int Alpha) // Description of the user-defined function // Start of the function body for (int i=1; i<=alpha; i++) // Cycle operator // Start of the cycle body int a = 2*i + 3; // Assignment operator Alert ("a=", a); // Standard function call operator // End of the cycle body // End of the function body Ebben az esetben, a függvény abban a pillanatban be fog fejeződni, amikor a 'for' ciklusoperátor végrehajtása véget ért. Az utolsó művelet a függvény végrehajtásban a ciklusoperátor feltételének tesztelése lesz. Amint a Feltétel a ciklusoperátor fejlécében hamissá válik, a vezérlés a ciklusoperátoron kívül kerül. Mivel a függvény testében levő utolsó végrehajtható operátor, ez a ciklusoperátor, a My_function () felhasználói függvény be fog fejeződni,és a vezérlés a függvényen kívülre kerül, oda ahonnan a függvényt hívtuk

124 Változók Bármilyen algoritmikus nyelven írunk programokat, a különböző változó típusok ismerete nagyon fontos. Ebben a részben elemezni fogjuk az MQL4-ben használt változó típusokat. Előre definiált változók és a RefreshRates függvény. Először is az előre definiált változókat fogjuk megismerni. Az előre definiált változók nevei foglaltak és nem használhatjuk öket egyéb változók neveiként. Az előre definiált változók nagyon fontos információt hordoznak, amelyek nélkülözhetetlenek az aktuális piaci helyzet elemzéséhez. Az előre definiált változók értékeinek frissítésére a RefreshRates() függvényt használjuk. A változók típusai. A változók nagyon fontosak egy program írásában. Ezek lehetnek helyiek és globálisak, külsők és belsők. A statikus változók a függvényhívások között is megőrzik az értékeiket, hasznos, hogy a lokális változó emlékszik az értékére anélkül, hogy globális változókat hoznánk létre. Az ügyfélterminál globális változói (GlobalVariables). A globális változók mellett, amelyeknek az értéki az adott program szintjén érhetők el, lehetnek olyan változók amelyek bármelyik program részéről elérhetők, ezek az ügyfélterminál globális változói. Az ilyen globális változókat GlobalVariables-nek (GV) nevezik. Lehetővé teszik az egymástól független MQL4 programok kölcsönhatását. Arra használhatjuk őket, hogy adatokat osszanak meg scriptek, indikátorok és Expert Advisorok között. A terminál bezárásakor a GlobalVariables értékei mentésre kerülnek és elérhetőek a MetaTrader 4 következő indításakor. Ha a GlobalVariables 4 héten keresztül nincs hívva a programok részéről, akkor azt törölni fogja a terminál. Tömbök. Ha nagy mennyiségű, azonos típusú adatot kell menteni vagy feldolgozni, akkor nem nékülözhetjük a tömböket. Mielőtt használnánk egy tömböt, azt deklarálni kell, ugyanúgy mint egy változót. A tömbelemek hivatását a tömb elem indexekkel végezzük. Az indexálást a nullától kezdjüt. A tömb dimenziók száma dimenzionalitásnak nevezzük. A tömb dimenziók száma maximum négy. A tömb értékeket egyértelműen kell inicializálni mert nehéz a hibákat lokalizál

125 Előre definiált változók és a RefreshRates függvény Az MQL4 nyelvben vannak előre definiált változó nevek. Az előre definiált változó egy változó előre definiált névvel, aminek az értékét az ügyfélterminál határozza meg, és nem változtathatjuk meg programozási módszerekkel. Az előre definiált változók visszatükrözik az aktuális chart (szimbólum ábra) állapotát abban a pillanatban, amikor a programjaink (Expert Advisor, script vagy egyéni indikátor) indulnak, vagy a RefreshRates() függvény végrehajtódik. Az előre definiált változók listája: Ask - az aktuális szimbólum utoljára ismert eladási ára; Bid - az aktuális szimbólum utoljára ismert vételi ára; Bars - egy aktuális ábrán levő bárok száma; Point egy pont értéke az aktuális pénznemben; Digits az aktuális szimbólum árában levő tizedespont utáni számjegyek száma. Az előre definiált tömbök nevei: Time - az aktuális bár nyitási ideje; Open - az aktuális bár nyitó ára; Close - az aktuális bár záró ára; High - az aktuális bár legnagyobb ára; Low - az aktuális bár minimális ára; Volume - az aktuális bárban lévő tickek száma. (a tömbök fogalmát és az előre definiált tőmböket az Arrays részben ismertetjük). Az előre definiált változók tulajdonságai Az előre definiált változók neveit nem lehet a felhasználó által létrehozott változók azonosítására használni. Az előre definiált változókat használhatunk a kifejezésekben a változókra vonatkozó szabályok szerint, de egy előre definiált változó értékét nem változtathatjuk meg. Ha megpróbálunk lefordítani egy olyan programot, ami egy olyan értékadó operátort tartalmaz, amelyben egy előre definiált változót helyeztek egy egyenlőségjel jobb oldalán, a Meta Editor hibaüzenetet fog adni. Elérhetőség tekintetében az előre definiált változók globális változók, ezek bármely programrészről elérhetők (lásd:a változók típusai). Az előre definiált változók legfontosabb tulajdonságai a következők: Minden előre definiált változó értékét automatikusan frissíti az ügyfélterminál, abban a pillanatban, amikor a különleges függvényeket elindítja. Az előre definiált változók előző és aktuális értékei egyenlők lehetnek, de magát az értéket frissíteni fogják. Egy különleges függvény végrehajtásának legelső pillanatában ezeket a változókat frissítik és már az első programsorokból elérhetők. Hagy illusztráljuk az előre definiált változó frissítését a következő példán (predefined.mq4 Expert Advisor):

126 // // predefined.mq4 // The code should be used for educational purpose only. // int start() // Special funct. start Alert("Bid = ", Bid); // Current price return; // Exit start() // Könnyű észrevenni, hogy ez a program a Bid változó értékeit mutatja a riasztási ablakban, amely minden alkalommal egyenlő lesz az aktuális árral. Ugyanezzel a módszerrel tudod ellenőrizni más változók értékeit. Például az Ask változó szintén a pillanatnyi ártól függ. A Bars változó értéke szintén változni fog, ha a bárok száma változik. Ez annál a ticknél történik, aminél egy új bár kezdődik az ablakban. A Point értéke egy instrumentum tulajdonságainak részletes leírástól függ. Például EUR/USD-nél ez az érték , USD/JPYnél A Digits érték ezeknél a valutapároknál egyenlő 4-gyel illetőleg 2-vel. Itt van az előre definiált változók egy másik fontos tulajdonsága: Az ügyfélterminál az előre definiált változók készletét külön-külön biztosítja mindegyik elindított program számára. Mindegyik elindított program a saját történelmi adatait használja. Ha egy ügyfélterminálban több alkalmazási program (Expert Advisor, script, indikátorok) fut egyszerre, az ügyfélterminál mindegyiküknek külön-külön létre fogja hozni az előre definiált változók (történelmi adatok) értékeinek különálló készletét. Elemezzük részletesen ennek a megoldásnak az okait. Az 52. ábra a különleges start() függvénykülönböző végrehajtás hosszát mutatja két Expert Advisor végrehajtása során. Az egyszerűség kedvéért tegyük fel, hogy a vizsgált Expert Advisorokon kívül nincs másik különleges függvény és minkét Expert Advisors ugyanannak a szimbólumnak azonos idősíkján működik (különböző ablakokban). 52. ábra. A start() függvény műveleti ideje kisebb vagy nagyobb lehet két tick közötti időintervallumnál. Az Expert Advisorok különbözhetnek a start() függvényük végrehajtás idejében. Közepes szintű Expert Advisornál ez az időtartam milliszekundum. Vannak Expert Advisorok amelyeknél ez másodperceket vagy tíz másodperceket is igénybe vesz. A tickek között időintervallum szintén különböző: több milliszekundum, több perc és néha több tíz perc. Elemezzük az adott példán, hogyan befolyásolja a tickek érkezése az Expert Advisor 1 és Expert Advisor 2 start() függvényeinek végrehajtását. A t 0 időpillanatnál az Expert Advisor 1-et hozzácsatoljuk az ügyfélterminálhoz és az tickváró módba kerül. Amint a t 1 tick megérkezik, a terminál elindítja a különleges start() függvényt. Azzal együtt a program megkapja az előre definiált változók frissített készletét. Végrehajtása alatt a program használhatja ezeket az értékeket, ezek változatlanok fognak maradni a start() műveleti ideje alatt. Miután start() befejezi a futását, a program ismét tick váró módba fog kerülni. Az a legközelibb esemény, aminél az előre definiált változók új értékeket kaphatnak egy új tick. Az Expert Advisor 1 start() függvényének végrehajtási ideje T1 lényegesen rövidebb, mint a tickek érkezése közötti időköz, például: t1 - t2 vagy t2 - t3,, stb.. Tehát, a vizsgált Expert Advisor 1 végrehajtási időszaka alatt

127 nem alakul ki olyan helyzet, amiben az előre definiált változók értékei elavulnak, vagyis (utoljára ismert) értékei különböznének az aktuális helyzettől. Az Expert Advisor 2-nél más a helyzet, mert a start () függvény végrehajtási ideje T2 néha felülmúlja a tickek közötti időközt. Az Expert Advisor 2 start()függvényét szintén a t 1 pillanatban indítják el. Az 52. ábrán láthatjuk, hogy a t 1 - t 2 tickek közötti idő nagyobb a start() T2 végrehajtásnál, ezért a program végrehajtásának ez alatt az időszaka alatt az előre definiált változókat nem frissítették (ebben az időszakban a szerverről új értékek nem származnak, úgyhogy azokat az értékeket kell használni, amiket a t 1 pillanatban kaptunk). Az Expert Advisor 2-t legközelebbi végrehajtása a t 2 pillanatban kezdődik, amikor a második ticket megkapjuk. Ezzel együtt az előre definiált értékek készletét szintén frissítik. 52. ábrában látjuk, hogy a t 31 pillanat, az új tick érkezése a start() végrehajtási időszakán belül van. Felmerül egy kérdés: Mi lesz az Expert Advisor 2 számára elérhető előre definiált változók értéke a t 3 időponttól (mikor a harmadik tick érkezik) a t 32 időpontig, amikor start() befejezi a működését? A választ a következő szabállyal összhangban megtalálhatjuk: Az előre definiált változók értékeit mentik a különleges függvények végrehajtásának egész időszaka alatt. Ezeket az értékeket erőszakosan frissíthetjük a RefreshRates() beépített függvény használatával. Így (ha RefreshRates() függvényt nem hajtották végre) a start() végrehajtásának az egész időszaka alatt az Expert Advisor 2 az előre definiált változók azon készletét használja, amit akkor mentett, amikor a második ticket megkapta. Bár a két Expert Advisor ugyanabban az ablakban működik, a t 3 pillanattól kezdve a két EA az előre definiált változók különböző értékeivel fog működni. Az Expert Advisor 1 a t 3 pillanatban érkező új adatokkal fog dolgozni, míg az Expert Advisor 2 azokat az adatokat használja, amelyek a t 2 pillanatban érkeztek. A hosszabb program végrehajtási idő és a rövidebb tickek közötti időtartam esetén nagyobb a valószínűsége annak, hogy a következő tick a programvégrehajtás-időszak alatt fog jönni. A történelmi adatok készletének helyi másolata mindegyik program számára lehetővé teszi, az előre definiált változók álladóságát minden különleges függvény teljes végrehajtási ideje alatt. A t 4 pillanattól kezdve, amikor a következő tick jön, mindkét EA megint el fog indulni az előre definiált változók azon saját készletének használatával, amelyeket a t 4 pillanatban (amikor a negyedik tick jött) megkaptak. RefreshRates() függvény bool RefreshRates() A RefreshRates() beépített függvény lehetővé teszi, hogy frissítsük a történelmi adatok értékeinek helyi másolatát. Más szóval ez a függvény erőszakosan frissíti az aktuális piaci adatokat (Volume, az utolsó szerver idő Time[0], Bid, Ask, stb.).ezt a függvényt akkor használhatjuk, amikor egy program sokáig végzi a számításait és eközben az adatokat frissíteni kell. A RefreshRates() függvény TRUE-t küld vissza, ha a végrehajtása pillanatában a terminálnak van új információja (új történelmi adatokkal kapcsolatos információ, ha egy új tick jött a különleges függvények végrehajtás alatt). Ez esetben az előre definiált változók helyi másolatainak a készletét frissíteni fogjak. RefreshRates() FALSE-ot küld vissza, ha a végrehajtásának pillanatáig az ügyfélterminál történelmi adatait még nem frissítették. Ekkor az előre definiált változók helyi másolatai nem változnak. Vedd észre, hogy RefreshRates() csak azt a programot befolyásolja, amiben azt elindítják, (és nem minden programot, ami egy időben fut az ügyfélterminálban)

128 Illusztráljuk a RefreshRates() végrehajtást egy példán! 21. feladat: Számolja ki az olyan ismétlések a számát, amelyeket a ciklusoperátor a tickek között végre tud hajtani, (egymást követő 5 tick). Ezt a feladatot csak a RefreshRates() használatával oldhatjuk meg (countiter.mq4 script ): // // countiter.mq4 // The code should be used for educational purpose only. // int start() // Special funct. start() int i, Count; // Declaring variables for (i=1; i<=5; i++) // Show for 5 ticks Count=0; // Clearing counter while(refreshrates()==false) // Until... //..a new tick comes Count = Count+1; // Iteration counter Alert("Tick ",i,", loops ",Count); // After each tick return; // Exit start() // A problémafelvetés feltételei szerint a számításokat csak a legközelebbi öt tick esetén kell elvégezni, ezért tudunk scriptet használni. Két változót használunk a programban: i a tickeket számolja és a Count számolja az ismétléseket. A külső ciklus a feldolgozott tickek számával összhangban szervezett (1-től 5-ig). A for-ciklus elindítja az ismétlések számolását (a while belső ciklust) és kijelzi a riasztási ablakban az ismétlések és a tickek számát. A belső 'while' ciklus addig fog működni, amíg a RefreshRates() visszatérési értéke FALSE (hamis) vagyis amíg egy új tick nem jön. A while' futása alatt (a tickek közötti időközben) a Count értéke állandóan növekedni fog; vagyis a 'while' ciklusokat fogja számolni. Ha valamely pillanatban a RefreshRates() visszatérési értéke TRUE (igaz) ez azt jelenti, hogy az előre definiált változók új értéket kaptak az ügyfélterminálban, vagyis egy új tick jött. Ekkor a vezérlés a 'while' cikluson kívülre kerül és a számolás így kész. A countiter.mq4 script végrehajtásnak eredményeképpen megjelenik egy riasztási ablak: 53. ábra. Az EUR/USD charton dolgozó countiter.mq4 script eredményei

129 Láthatjuk, hogy 1 másodperc alatt (a negyedik és az ötödik tick közötti időköz) a script 3 milliónál is több ismétlést hajtott végre. Hasonló eredményeket kapunk más tickeken végzett egyszerű számításoktól. Térjünk vissza az előző példához ( predefined.mq4 Expert Advisor). Korábban láttuk azt, hogyha a RefreshRates()-t nem hajtjuk végrea z Expert Advisor 2-ben, akkor az előre definiált változók helyi másolatainak az értékei változatlanok fognak maradni a start() végrehajtás egész időszaka alatt. Példánkban a t 2 időponttól a t 32időpontig. Ha a harmadik tick után (amikor start() fut) a RefreshRates() függvény végrehajtjuk például a t 31időpontban, a helyi másolatok értékei frissítésre kerülnek. A start függvény végrehajtásából fennmaradó időszak alatt, a t 31 (RefreshRates() végrehajtás) és a t 32 (a start() végrehajtás vége) időpontok között, az előre definiált változók helyi másolatainak az új értékei lesznek elérhetők az Expert Advisor 2 számára, amiket az ügyfélterminál a t 3 időponttól kezdődően biztosít. Ha az Expert Advisor 2-ben a a RefreshRates() függvényt a t 11 vagy a t 21 időpontban hajtjuk végre (akkor amikor a start() függvény kezdete óta még nem volt újabb tick), az előre definiált változók helyi másolatai nem fognak megváltozni. Ilyen esetben az előre definiált változók helyi másolatainak az értékei egyenlők lesznek azokkal az értékekkel, amelyeket az ügyfélterminál a start() különleges függvény utolsó indítása pillanatában meghatározott

130 A változók típusai Egy MQL4 alkalmazási program tartalmazhat több tíz vagy több száz változót. Mindegyik változó egy nagyon fontos tulajdonsága az a lehetőség hogy az értékét a program felhasználja. Ennek a lehetőségnek a korlátozása határozza meg a változó hatókörét. A változó hatóköre a program azon része, ahonnan a változó értéke elérhető. Minden változónak van hatóköre. Hatókörük szerint a változóknak az MQL4-ben két fajtája van: helyi és globális. Helyi (lokális) és globális változók Lokális változó: a változót egy függvényen belül deklarálták. A lokális változók hatóköre az a függvénytörzs, amiben a változót deklarálták. A lokális változót lehet inicializálni egy állandó vagy egy kifejezés megfelelő típusával. A globális változó egy olyan változó, amit mindenen függvényen kívül deklaráltak. A globális változók hatóköre az egész program. Egy globális változót csak egy hasonló típusú állandóval lehet inicializálni (kifejezéssel nem). A globális változókat csak egyszer inicializáljuk a különleges függvények végrehajtása előtt. Ha a program vezérlése egy bizonyos függvényben van, annak a lokális változónak az értéke, amit egy másik függvényben deklaráltak, nem elérhető. Bármilyen globális változó értéke elérhető bármilyen különleges és felhasználói függvényből. Nézzünk egy egyszerű példát! 22. feladat Hozz létre egy olyan programot, ami tickeket számol! A 22. feladat megoldási algoritmusa, ami egy globális változót használ (countticks.mq4): // // countticks.mq4 // The code should be used for educational purpose only. // int Tick; // Global variable // int start() // Special function start() Tick++; // Tick counter Comment("Received: tick No ",Tick); // Alert that contains number return; // start() exit operator // Ebben a programban csak egy globális változót használunk: Tick. Ez azért globális, mert azt start() leírásán kívül deklaráljuk. Ez azt jelenti, hogy a változó meg fogja őrizni az értékét tickről tickre. Lássuk a programvégrehajtás részleteit! A Különleges Függvények részben megismertük azokat a feltételeket, amelyek a különleges függvényeket elindítják. Röviden: Expert Advisorban a start() függvényt elindítja az ügyfélterminál, amikor egy új tick jön. Az Expert Advisornak a charthoz való csatolásának a pillanatánál a következő események történnek: 1. A Tick globális változó deklarációja. Ezt a változót nem inicializálja egy állandó, az értéke ebben az esetben egyenlő nullával. 2. A vezérlést magánál tartja az ügyfélterminál, amíg egy új tick nem jön. 3. A ticket megkaptuk. A vezérlés átkerül a különleges start() függvénybe A start() függvény törzsében a vezérlés átkerül az első operátorhoz: Tick++; // Tick counter

131 Az operátor végrehajtásnak eredményeképpen Tick értéke növekszik 1-el (egy lesz) A vezérlést megkapja a következő operátor: Comment("received: tick No ",Tick); // Alert that contains number A Comment() beépített függvény végrehajtása egy feliratot jelenít meg: Received: tick No A vezérlés tovább kerül a következő operátorra: return; // start() exit operator A return végrehajtása következtében a start() befejezi a műveletet, és a vezérlés vissza tér az ügyfélterminálhoz. A globális változó továbbra is létezik, megtartj aaz értékét ami egyenlő 1-gyel. A további műveletek a 2. ponttól fognak ismétlődni. Ismét a Tick változót használjuk a számításokban, de a második ticknél, vagyis a start() kezdődő pillanatban a Tick változó értéke egyenlő 1-el ezért az operátor újbóli végrehajtásának az eredménye: Tick++; // Tick counter a Tick változó egy új értéke lesz, - azt növelni fogják 1-el és most egyenlő lesz 2-vel, a Comment() függvény végrehajtása az új értéket fogja megjeleníteni: Received: tick No 2 Így Tick értéke növekedni fog 1-el a különleges start() függvény mindegyik indításánál, vagyis mindegyik ticknél. Az ilyen feladat megoldása csak úgy lehetséges, ha olyan változót használunk, ami megőrzi az értékét miután kiléptünk egy függvényből, (ebben az esetben egy globális változót használtunk). Értelmetlen erre a célra lokális változót használni: egy lokális változó elveszti az értékét, ha a függvény, amiben deklarálták, befejezi a műveletét. Ezt könnyen beláthatjuk, ha elindítunk egy Expert Advisort, amiben Ticket helyi változó (a program egy algoritmikus hibát tartalmaz): int start() // Special function start() int Tick; // Local variable Tick++; // Tick counter Comment("Received: tick No ",Tick);// Alert that contains number return; // start() exit operator A szintaxisa alapján nincsenek a kódban hibák. Ezt a programot sikeresen lefordíthatjuk és elindíthatjuk. Működni fog, de az eredmény minden alkalommal ugyanaz lesz: received tick No 1 Ez természetes, mert a Tick változót a különleges start() függvény mindegyik indításánál inicializálni fogjuk nulla értékre. A Tick változó minden további, egyel történő növekedése ellenére: a kijelzett érték mindig egyenlő lesz 1-gyel. Statikus változók A fizikai szinten a lokális változókat a hozzájuk tartozó függvények ideiglenes memóriarekeszeiben tároljuk. Van mód arra, hogy egy változót, amit egy függvényen belül deklaráltak az állandó programmemóriában lokalizáljunk, - a deklaráció során módosítani kell a változó típusát a 'static' szó beszúrásával: static int Number; // Static variable of integer type Lent látható a 22. feladat megoldása statikus változó használatával ( staticvar.mq4 Expert Advisor ):

132 // // staticvar.mq4 // The code should be used for educational purpose only. // int start() // Special function start() static int Tick; // Static local variable Tick++; // Tick counter Comment("Received: tick No ",Tick); // Alert that contains number return; // start() exit operator // A statikus változókat egyszer inicializáljuk. Mindegyik statikus változót csak egy megfelelő típusú állandó inicializálhatja(ebben különbözik egy egyszerű lokális változótól, mert azt inicializálhatja bármilyen kifejezés is). Ha inicializálása nincs, egy statikus változó kezdeti értéke nulla. A statikus változókat egy állandó memóriarészben raktározzák el, az értéke nem veszik el, ha kilépünk a függvényből. Azonban a statikus változóknak vannak olyan korlátozásaik, amelyek a lokális változókra jellemzőek, - a statikus változó hatásköre az a függvény, amiben a változót deklarálják, ebben különböznek azoktól a globális változóktól, amelyeknek az értékei elérhetőek az egész programból. Figyeld meg, hogy a countticks.mq4 és astaticvar.mq4 programok azonos eredményt adnak. Minden tömb eredendően statikus, vagyis ezek statikus típusúak, még akkor is, ha azt nyíltan nem jelezzük ( lásd: Tömbök). External (külső) változók External (külső) változó egy olyan változó, ami a program tulajdonságok ablakából elérhető. Egy külső változót minden függvényen kívül deklarálunk és hatókörük globális - az egész programból elérhetők. Amikor deklarálunk egy külső változót, ezt az érték típusa előtt az 'extern' szóval kell jelezni: extern int Number; // External variable of integer type A külső változókat a program fejrészében deklaráljuk, mégpedig minden olyan függvény leírása előtt, amely függvény használja az adott külső változót. A külső változók használata nagyon kényelmes, erre időről időre szükség van, ha egy programot különböző változó értékekkel kell elindítanunk. 23. feladat: Hozzunk létre egy programot, amiben a következő feltételeket teljesülnek: ha egy ár elért egy bizonyos szintet (Level) és visszament e szint alá nponttal, ezt a tényt jelenteni kell a kereskedőnek. Nyilvánvaló ebben a feladatban szükség van arra, hogy a beállításokat meg tudjuk változtatni, mert a mai árak különböznek a tegnapiaktól; és a holnapiak különbözni fognak a maiaktól. A változó külső beállításának opcióját az externvar.mq4 Expert Advisorban alkalmazzuk: // // externvar.mq4 // The code should be used for educational purpose only. // extern double Level = ; // External variable extern int n = 5; // External variable bool Fact_1 = false; // Global variable bool Fact_2 = false; // Global variable // int start() // Special function start() double Price = Bid; // Local variable if (Fact_2==true) // If there was an Alert.. return; //..exit if (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits)) Fact_1 = true; // Event 1 happened

133 if (Fact_1 == true && NormalizeDouble(Price,Digits)<= NormalizeDouble(Level-n*Point,Digits)) My_Alert(); // User-defined function call return; // Exit start() // void My_Alert() // User-defined function Alert("Conditions implemented"); // Alert Fact_2 = true; // Event 2 happened return; // Exit user-defined function // Ebben a programban a következő sorokban külső változókat helyeztünk el: extern double Level = ; // External variable extern int n = 1; // External variable A külső változók értékei elérhetők a program paraméter ablakából. Ezeknek a változóknak az előnye az, hogy bármikor meg tudjuk ezeket változtatni - a szimbólum ablakhoz csatolt program végrehajtása alatt is. 54. ábra. Program tulajdonságok ablak; itt változtathatjuk meg a külső változók értékeit. Amikor csatoljuk a programot egy szimbólumablakhoz, a programkódban lévő külső változók értékeit a program paraméter ablakában fogjuk látni. A felhasználó meg tudja változtatni ezeket az értékeket. Amikor a felhasználó az OK-ra kattint, a programot el fogja indítani az ügyfélterminál. A külső változók értékei azok az értékek lesznek, amelyeket a felhasználó a paraméterablakban lát. A futása alatt ezeket az értékeket megváltoztathatja a végrehajtott program. Ha a felhasználónak a program futása alatt meg kell változtatnia a külső változók értékeit, a paraméterablakot ki kell nyitni és változtatásokat el kell végezni. Figyelembe kell venni, hogy a program tulajdonságok ablakot csak abban az időszakban lehet kinyitni mikor a program (Expert Advisor vagy indikátor) egy új tickre várakozik, vagyis semelyik különleges függvény nem fut. A program végrehajtási időszak alatt ezt az eszköztárt nem nyithatjuk ki. Ezért ha egy programot úgy írnak, hogy a végrehajtása túl hosszú (több másodperc vagy tíz másodpercek), a felhasználó nehézségekkel nézhet szembe, amik megpróbál hozzáférni a paraméter ablakhoz. A scriptek külső változóinak az értékei csak akkor elérhetőek amikor a programot csatoljuk egy charthoz, azonban nem tudjuk megváltoztatni őket a script végrehajtása alatt. Ha a paraméter ablak nyitva van, az Expert Advisor nem dolgozik, a vezérlést az ügyfélterminál magánál tartja, és nem adja át a különleges függvényeket tartalmazó programba

134 Jegyezd meg, amikor egy EA tulajdonságok ablak nyitva van és a felhasználó a külső változók értékeit kezeli, az EA (vagy indikátor) nem dolgozik. A külső változók értékeinek beállítása után és az OK-ra kattintva a felhasználó újra lefuttatja a programot. A tulajdonságok ablak bezárása után ügyfélterminál egymás után elkezdi a különleges függvények végrehajtását, először a deinit(), azután az init() függvény, végül mikor egy új tick jön a start(). A deinit() végrehajtásánál, amikor befejezünk egy programot, a külső változók értékei az előző munkamenetből származnak, azok elérhetők amikor az EA beállítási ablakot kinyitjuk. Az init() végrehajtása előtt a külső változók új értékeket kaphatnak és az init() végrehajtás azokkal az új külső változó értékekkel történik, amelyeket beállított a felhasználó. Így a külső változók új értékei az új munkamenet kezdetétől (init - start - deinit) válnak érvényessé, az Expert Advisor új munkamenete az init() végrehajtásával kezdődik. Az a tény, hogy kinyitottunk egy beállítási ablakot, nem befolyásolja a globális változók értékeit. Az alatt az idő alatt, amikor az ablak nyitva van, és miután az ablakot bezártuk, a globális változók megőrzik azokat az értékeiket, amik a beállítási ablak kinyitásakor érvényesek voltak. Az externvar.mq4 programban egy helyi és két globális változót használunk. bool Fact_1 = false; // Global variable bool Fact_2 = false; // Global variable double Price = Bid; // Local variable A problémamegoldás algoritmusa így néz ki. Két eseményt azonosítunk: az első a Level elérése, és a második, a figyelmeztetés (Level-nél n ponttal alacsonyabb szintre történő visszaesés). Ezeket az eseményeket Fact_1 és Fact_2 változók értékeiben jelenítjük meg: ha az esemény nem történt meg, a változó értéke false (hamis), minden más esetben true(igaz).a következő sorokban: if (NormalizeDouble(Price,Digits) >= NormalizeDouble(Level,Digits)) Fact_1 = true; // Event 1 happened azt a tényt, hogy az első esemény megtörtént, megállapítottuk. A NormalizeDouble() egy beépített függvény, ami lehetővé teszi, hogy a változók értékeivel meghatározott pontossággal végezzük a számításokat, (a szimbólum árának pontossága). Ha az ár egyenlő vagy magasabb a jelezett szintnél az első feltétel teljesül és a Fact_1 globális változó true (igaz) értéket kap. A program úgy van felépítve, hogy ha Fact_1 egyszer a true értéket kapja, azt soha ne változtassa meg hamissá (false) - nincs az ezzel kapcsolatos kód a programban. A következő sorokban: if (Fact_1 == true && NormalizeDouble(Price,Digits)<= NormalizeDouble(Level-n*Point,Digits)) My_Alert(); // User-defined function call az üzenet megjelenítése van kódolva. Ha az első feltétel teljesül és az ár n pont esett (annyit vagy többet) a jelezett szintről egy üzenetet fog megjeleníteni a My_Alert() felhasználói függvény. A felhasználói függvényben az üzenet megjelenítése után, a Fact_2 változó true értéket kap. Majd a felhasználói függvény és ezután a különleges start() függvény is véget ér. A miután a Fact_2 változó a true értéket kapja, a program végleg be fogja fejezni a munkáját, az egyszer már bemutatott üzenetet nem fogja megismételni ez alatt a munkamenet alatt mert: if (Fact_2==true) // If there was an Alert.. return; //..exit Nagy jelentősége van ebben a programban annak a ténynek, hogy a globális változók értékeit bárhonnan megváltoztathatjuk, (a különleges és a felhasználói függvényekből egyaránt) és az értékük megmarad a programoperáció egész időszaka alatt, a tickek közti szünetekben, a külső változók módosítása után és az idősíkok váltása után is. Általában a globális változók értékeit bármelyik különleges függvényből megváltoztathatjuk. Ezért kell rendkívül figyelmesnek lenni, az olyan operátorokkal, amelyek megváltoztathatják a globális változók értékeit az init()-ben és a deinit()-ben. Például ha lenullázzuk egy globális változó értékét az init() függvényben, a start() első végrehajtásánál ennek a változónak az értéke egyenlő lesz nullával, tehát azok az értékek, amit az előző start() végrehajtás alatt megszereztek ezek a változók elvesznek (például az extern változók módosítása után)

135 Az ügyfélterminál globális változói (GlobalVariables) Több alkalmazási program működhet az ügyfélterminálban egyidejűleg. Néhány esetben szükségessé válhat néhány adat átadása egyik programból a másikba. Történetesen ezért vannak az MQL4-ben az ügyfélterminálnak globális változói. Global variable of Client Terminal egy változó, az az érték, ami elérhető minden alkalmazási programból, amit az ügyfélterminálban elindítottak (rövidített alak: GV). Jegyezd meg: az ügyfélterminál globális változója és a globális változó különböző változó típusok hasonló nevekkel. A globális változók hatóköre az a program, amiben a változót deklarálják; míg az ügyfélterminál globális változóinak a hatóköre minden olyan program, ami az ügyfélterminálban fut. GlobalVariables tulajdonságai A GV abban különbözik az egyéb változóktól, hogy bármely programból nemcsak létre lehet hozni, hanem törölni is. A GV értékét a merevlemezen tárolják és mentik az ügyfélterminál bezárásakor. A deklarált GV-t az ügyfélterminál, az utolsó hívástól számított 4 héti tárolja. Ha ez alatt az időszak alatt semelyik program sem használja a változót, az ügyfélterminál törli azt. A GV csak double típusú lehet. A GlobalVariables függvényei GV-vel való együttműködésre az MQL4-nek van egy függvény készlete (lásd még: GlobalVariables). Elemezzük azokat, amelyeket a további példákban használni fogunk! GlobalVariableSet() függvény datetime GlobalVariableSet( string name, double value) Ez a függvény létrehozza egy globális változó egy új értékét. Ha a változó nem létezik, a rendszer létrehozza az új globális változót is. Sikeres végrehajtás esetében a függvény visszaküldi az utolsó hozzáférés idejét, egyéb esetben a visszatérési érték 0. Hibainformációt a GetLastError() függvény hívásával kaphatunk. Paraméterek: name - A globális változó neve. value Az új számszerű érték. GlobalVariableGet() függvény double double GlobalVariableGet( string name) A függvény visszaküldi egy létező globális változó értékét vagy hiba esetében 0-t küld vissza. Hibainformációt, a GetLastError() függvény hívásával kaphatunk. Paraméterek: name - A globális változó neve. GlobalVariableDel() függvény bool GlobalVariableDel( string name) Ez a függvény töröl egy globális változót. Egy sikeres törlés esetében a függvény visszaküldi atrue-t egyéb esetben FALSE-t. Hibainformációt most is a GetLastError() függvény hívásával kaphatunk. Paraméterek:

136 name - A globális változó neve. Hogy szemléltessük a GlobalVariables használatának előnyeit, oldjuk meg a következő feladatot: 24. feladat: Több Expert Advisor dolgozik egyszerre a terminálban. A letét dollár. Az összes nyitott megbízás által lekötött tőkének nem szabad meghaladnia a letét 30 százalékát. Egyenlő összeget kell elkülöníteni mindegyik Expert Advisor részére. Hozz létre egy olyan EA programot, ami kiszámítja a kerekedésre elkülönített összeget. Egy EA részére elkülönítettek összeg kiszámítása nem nehéz. Azonban ehhez szükségünk van arra, hogy tudjuk azon Expert Advisorok számát amelyek egy időben működnek. Nincs olyan MQL4 függvény ami választ adhatna erre a kérdésre. Az egyetlen lehetőség arra, hogy kiszámoljuk az elindított programok számát az, hogy mindegyik programnak be kell jelentenie magát azáltal, hogy megváltoztatja egy bizonyos GV értékét. Minden program elküldi ezt a szükséges információt, ehhez a GV-hez, és ez jelzi az aktuális állapotot. Itt kell megjegyezni, hogy nem minden program támogatja ennek a feladatnak a megoldását. Ha egy Expert Advisor nem jelentkezik be, akkor nem fog részt venni a számításban. Ebben a feladatmegoldásban csak azok az EA-k használhatók, amelyek tartalmazzák a szükséges kódot - amellyel meg tudják változtatni a GV értéket, írni és olvasni tudják ezt a változót. Itt van egy olyan Expert Advisor (globalvar.mq4), amely a 24. feladat megoldása során demonstrálja a GlobalVariables használatát: // // globalvar.mq4 // The code should be used for educational purpose only. // int Experts; // Amount of EAs double Depo= , // Set deposit Persent=30, // Set percentage Money; // Desired money string Quantity="GV_Quantity"; // GV name // int init() // Special funct. init() Experts=GlobalVariableGet(Quantity); // Getting current value Experts=Experts+1; // Amount of EAs GlobalVariableSet(Quantity, Experts); // New value Money=Depo*Persent/100/Experts; // Money for EAs Alert("For EA in window ", Symbol()," allocated ",Money); return; // Exit init() // int start() // Special funct. start() int New_Experts= GlobalVariableGet(Quantity);// New amount of EAs if (Experts!=New_Experts) // If changed Experts=New_Experts; // Now current Money=Depo*Persent/100/Experts; // New money value Alert("New value for EA ",Symbol(),": ",Money); /*... Here the main EA code should be indicated. The value of the variable Money is used in it.... */ return; // Exit start() //

137 int deinit() // Special funct. deinit() if (Experts ==1) // If one EA.. GlobalVariableDel(Quantity); //..delete GV else // Otherwise.. GlobalVariableSet(Quantity, Experts-1); //..diminish by 1 Alert("EA detached from window ",Symbol()); // Alert about detachment return; // Exit deinit() // Ez az EA három különleges függvényt tartalmaz. Röviden: minden különleges függvényt elindít az ügyfélterminál: az init() függvényt amikor az EA-t csatoljuk a charthoz, a deinit()-et mikor az EA-t leválasztjuk az ablakból, a start()-ot akkor amikor a tickek jönnek. A program fejrész tartalmazza a globális változók deklarációját (az ilyen változók hatóköre ez a program). A pénz elosztása az EA-k között egy változó értékétől függ ez az egyidejűleg dolgozó EA-k száma. Az a GV, amely az EA-k számát visszatükrözi, ebben a sorban van meghatározva: string Quantity = "GV_Quantity"; // GV name Megjegyzés: A GlobalVariable nevét egyeztetni kell a program létrehozásakor a többi programmal (az egyéb változók neveit a programozó szabadon megválaszthatja). Elemezzük részletesen, hogy a Quantity változó értéke hogyan befolyásolja a program végrehajtását! Amikor egy EA-t hozzácsatolnak egy ablakhoz be kell jelentenie a létezését, hogy a többi EA a terminálban tudhasson erről. Ennek korán meg kell történnie (lehetőleg az EA csatolásának pillanatában. Ezért a legjobban hely a számára az init() különleges függvény. Ennek a függvénynek az első sorában az EA kéri a Quantity GV változó értékét, és ezt az értéket kapja az Experts változó, erre a célra a GlobalVariableGet()függvényt használjuk: Experts = GlobalVariableGet(Quantity); // Getting current value Most az EA az Experts változó értékét, függetlenül annak értékétől növelni fogja 1-el. Ezzel jelzi az EA, hogy csatolták egy ablakhoz és a terminálban az egyidejűleg működő EA-k száma 1-el nőtt: Experts = Experts+1; // Amount of EAs Az Experts globális változót kényelemesen használhatjuk, de csak ebben a programban. Az értékét nem érik el másik EA-k. A Quantity GV értékének megváltozatására a GlobalVariableSet() függvényt kell használni amellyel beállítjuk a GV új értéket: GlobalVariableSet(Quantity, Experts); // New value Ez azt jelenti, hogy az Experts új értékét adjuk a Quantity GV-nek. Most ez az új GV érték rendelkezésére áll minden programnak, ami a terminálban működik. Azután kiszámolja (a legutóljára csatolt EA) azt az összeget, ami egy kereskedő EA-nak jut és létrehoz egy riasztást (itt riasztást csak azért alkalmazzuk, hogy illusztrálni tudjuk mikor és milyen események történnek, a tényleges programokban a riasztást csak szükség esetén alkalmazzuk). Money = Depo*Persent/100/Experts; // Money for EAs Alert("For EA in the window ", Symbol()," allocated ",Money); Jegyezd meg, az EA-nk csak a csatolt EA-k alapján számította ki a kívánt összeget (önmagát is beleértve). Amikor az init() végrehajtás kész, a vezérlés visszakerül az ügyfélterminálhoz és az EA elkezdésével vár egy új tickre. Amikor egy új tick jön,a terminál el fogja indítani a különleges start() függvényt. Most a feladatnak azt a részét hajtjuk végre ahol az EA folyamatosan számontartja a csatolt EA-k aktuális mennyiségét. Az Expert Advisorokat csatolhatjuk és eltávolíthatjuk; ennek következtében az egyidejűleg dolgozó EA-k száma változhat. Ettől függően az EA-nak újra kell számolnia azt az összeget, amit a feladat kiírásával összhangban kiutalhat. Tehát az első dolog, hogy mindegyik új ticknél az EA kéri a Quantity GV új értékét:

138 int New_Experts= GlobalVariableGet(Quantity);// New amount of EAs és ha a New_Experts változó új értéke különbözik az utoljára ismert Experts változótól, az új érték figyelembevételével újraszámoljuk a Money változó aktuális értékét, ezt az összeget kapja az EA, és létre hozzuk a megfelelő riasztást: if (Experts!= New_Experts) // If changed Experts = New_Experts; // Now current Money = Depo*Persent/100/Experts; // New money amount Alert("New value for EA ",Symbol(),": ",Money); Ha a New_Experts és Experts változók azonosak, számítást itt nem fojtatjuk az EA-kódjában (az 'if' operátor törzsét nem hajtjuk végre) a Money változó korábban kiszámított értékét használjuk továbbra is. A helyzettől függően minden ticknél vagy az újra kiszámolt Money értéket, vagy az előzőt használjuk. A 24. feladat korrekt megoldása érdekében mindegyik Expert Advisornak tájékoztatnia kell a többi Expert Advisort arról, ha eltávolították és a működő Expert Advisorok száma csökkent. Azonfelül, ha ez az utolsó EA a GV-t törölni kell. A deinit() végrehajtása jelzi az EA eltávolítását, az ennek megfelelő kódot kell létrehozni ebben a függvényben: int deinit() // Special funct. deinit() if (Experts ==1) // If one EA.. GlobalVariableDel(Quantity); //..delete GV else // Otherwise.. GlobalVariableSet(Quantity, Experts-1); //..diminish by 1 Alert("EA detached from window ",Symbol()); // Alert about detachment return; // Exit deinit() A deinit() függvényben minden számítást egy if operátoron belül végünk. Ha az EA-k száma egyenlő 1- gyel, vagyis ez az EA az utolsó, a GV-t töröljük a GlobalVariableDel() függvénnyel, más esetben (mikor az EA-k száma több 1-nél) a Quantity GV változó egy új, 1-el kisebb érték fog kapni a GlobalVariableSet() függvény segítségével. Azok az EA-k, amelyek működésben maradtak, a start() függvényük következő végrehajtása kezdetén észlelni fogják az új Quantity értéket és újraszámolják a Money változó értékét. Láthatjuk, hogy GlobalVariables értékeit olvashatjuk vagy módosíthatjuk bármelyik EA-ból a megfelelő függvények végrehajtásával. Közvetlen számítások GV értékekkel nem lehetségesek. Ha a GV értékét kell használ valamely kifejezésben, a GV értékét kell adni bármilyen másik változónak és a számításokban ezt a változót kell használni. Ennek megfelelően használjuk a két változót: Experts és New_Experts a következő sorokban: Experts = GlobalVariableGet(Quantity); // Getting current value int New_Experts= GlobalVariableGet(Quantity);// New amount of EAs Javaslom a lefordított globalvar.mq4 EA-t több ablakban elindítani. Az eseményektől függően a megfelelő tájékoztatást láthatjuk az Alert függvény ablakában. Például: 55. ábra. Riasztások az Alert ablakban, egymásután csatoljuk a globalvar.mq4 EA-t három különböző szimbólumablakhoz

139 Van egy opció az ügyfélterminálban, a Globális Változók eszköztár, ahol valós időben láthatunk minden létező Globális Változót és azok értékeit. Ez az eszköztár az ügyfélterminál menüjén keresztül érhető el Eszközök>> Globális Változók vagy (F3 billentyű): 56. ábra. Globális Változók eszköztára mikor a három globalvar.mq4 fut egyszerre. Ha az EA-kat eltávolítjuk, akkor az eszköztárból is eltűnik a bejegyzés az ügyfélterminál nyitott globális változóiról. Hibák a GlobalVariables használata során Ha a globalvar.mq4 EA-t különböző ablakokban indítjuk el egymás után és nyomon követjük az eseményeket, látni fogjuk, hogy a kód sikeresen működik. Azonban ez csak akkor igaz, ha a szünetek az események között eléggé nagyok. Figyeljük meg az 'if' operátort a deinit()-ben: if (Experts ==1) // If one EA.. Ebben az esetben az Experts globális változó értékét elemezzük. Bár ez visszatükrözi a GV értéket, de elavulttá is válhat (emlékezzünk, minden program valós idejű módban működik). Hogy megértsük az okokat lássuk a következő diagramot: 57. ábra Az EA-t leválasztjuk az EUR/USD ablakról a harmadi tick érkezése előtt. Az 57. ábrán látható, hogy az események alakulása hogyan érintik Quantity GV értékét. Lássuk, hogyan fog ez az érték változni attól függően, hogy mi történik. Tételezzük fel azt, hogy az EA végrehajtása a t 0 pillanatban kezdődött. Ebben a pillanatban a Quantity GV még nem létezik. A t 0 - t 1 időszakban az EA végrehajtja az init() különleges függvényt és létrehozza a Quantity GV-t, aminek az értéke t 1 időpontban egyenlő 1-gyel. Az EUR/USD következő tickjei elindítják a start() különleges függvényt. A t 0 - t 6 időszakban csak egy EA van az ügyfélterminálban ezért Quantity GV értéke nem változik. A t 6 időpontban a második EA-t hozzáerősítjük a GBP/USD charthoz. Az init() végrehajtása során a Quantity GV értéke megváltozik és a t 7 pillanatban egyenlő 2-vel. Azután a t 8pillanatban a harmadik EA-t is csatoljuk az USD/CHF charthoz ezért a t 9 pillanatban a Quantity GV egyenlő 3-mal. A t 10 időpontban a kereskedő úgy dönt, hogy leválasztja az EA-t az EUR/USD ablakról. Vegyük észre, hogy ennek az EA-nak az Experts változója, a start() legutolsó végrehajtása alatt kapott értékét őrzi, amit a

140 második ticknél a t 4 - t 5 időszakban kapott. A t 10 pillanatban az EUR/USD ablakban működő EA Experts változójának az értéke még mindig egyenlő 1-el. Ezért mikor a deinit() függvényt ez az EA végrehajtja, A Quantity GV-t a következő sorok végrehajtásával törölni fogja: if (Experts ==1) // If one EA.. GlobalVariableDel(Quantity); //..delete GV Így, bár kettő EA is dolgozik, GlobalVariable-t mégis törlik! Nem nehéz belátni, hogy ez milyen következményekkel jár a csatolt EA-k számításaira. A start() végrehajtásnál, ezek az EA-k azt fogják érzékelni, hogy a New_Experts értéke egyenlő nullával, ezért az Experts változó értékét szintén nullázni fogják. Végül Money értékét nem lehet kiszámolni, mert a képletben, amit számításokra használunk, az Experts van a nevezőben. Így a további számítások az EA-kban hibásak lesznek. Azonkívül az EA-k a deinit( ) függvények végrehajtásánál (mikor leválasztjuk őket a GBP/USD-ről és USD/CHF-ről) a GV-t megint létre fogják hozni, de a Quantity értéke az első EA leválasztásánál -1, a második leválasztásánál -2 lesz. Mindez a Money változó negatív értékéhez vezet. Ez fontos tény, mert a GV Quantity változó az EA-k bezárásától függetlenül továbbra is megmarad az ügyfélterminálban és a későbbiekben minden EA működésére hatással lesz az értéke. Egy másik eset is lehetséges. Az 58. ábra azt szemlélteti, hogy a GV értéke hogyan fog változni fog, ha az EA eltávolítása előtt még egy tick jön. 58. ábra. Az EA leválasztása a harmadik tick után az EUR/USD-ablakból. Az 58. ábrán látható események a t 0 - t 9 időszakban teljesen egybeesnek az 57. ábrán látható eseményekkel. A diagram alapján a t 12 pillanatban egy harmadik EUR/USD tick is jön, ennek következtében a start() függvény végrehajtása alatt az Experts értéke változni fog és egyenlő lesz 3-mal. Ezen EA-nak az EUR/USD chartról történő eltávolítás után, a deinit() végrehajtásnak eredményeképpen az Experts értéke 2 lesz, és a megmaradó EA-k helyesen működnek. Ezen érvelés alapján azt feltételezhetnénk, hogy a globalvar.mq4 EA-t helytelenül alkottuk. Az algoritmus hibája ebben az esetben az, hogy a tényleges helyzetet elemező Experts változó értéke nem mindig tükrözi vissza a ténylegesen működő EA-k összegét, mert minden esetben használják a deinit() függvényt. Az 58. ábrán leírt esetben a Experts értéke helyes, míg az 57. ábrán levő esetben nem. Tehát az EA végrehajtásának az eredménye véletlen eseményektől függ, a különböző szimbólumok tickjeinek véletlenszerű érkezésétől, amikkel az EA-k dolgoznak. Ebben az esetben a hibát könnyen meghatározhatjuk. Nekünk egyszerűen az elemzés előtt frissítenünk kell az Experts értékét (az if operátor végrehajtása előtt): int deinit() // Special funct. deinit() Experts = GlobalVariableGet(Quantity); // Getting current value if (Experts ==1) // If one EA.. GlobalVariableDel(Quantity); //..delete GV else // Otherwise.. GlobalVariableSet(Quantity, Experts-1); //..diminish by 1 Alert("EA detached from window ",Symbol()); // Alert about detachment return; // Exit deinit()

141 Az ilyen algoritmikus hibák veszélyesek, mert nem mindig nyilvánvalóak és nehéz észlelni őket. De ez nem jelenti azt, hogy egy felhasználónak mellőznie kellene a GlobalVariables használatát. Azonban bármilyen program kódjának építései közben számításba kell venni minden eseményt, ami befolyásolhatja a program működését. A szakmai gyakorlatban a globális változók használata nagy segítséget jelent: például segít tájékozódni egy másik szimbólumon történt kritikus eseményről (egy bizonyos árszint átlépése, letörése, stb.), letilthat egy másik Expert Advisort (a kereskedésre fordítható összeg megosztása céljából), vezetheti a több eszközön történő szinkronizált kereskedelmet. Az ügyfélterminál globális változóját létre tudjuk hozni egy indikátorból is néhány fontos eseményt értékelve; e változó értékét használhatja bármelyik Expert Advisor vagy script

142 Tömbök Annak az információnak a nagy részét, amit az alkalmazási programok használnak tömbök tartalmazzák. A tömbök fogalma Tömb az azonos típusú változók rendezett érték készletének az összefoglaló neve. A tömbök egy dimenziósak és többdimenziósak lehetnek. Egy tömbben levő dimenziók maximális száma négy lehet. Bármilyen adattípust elhelyezhetünk tömbökben. A tömbelem egy tömb része; egy indexelt változó, aminek van neve és valamilyen érték. 59. ábra. Az egész számtípus tömbjeinek a grafikus bemutatása: a) egy dimenziós; b) kétdimenziós; c) háromdimenziós. Indexelés A tömbelem-index egy vagy több egész értékű állandó, változó vagy kifejezés felsorolása vesszővel elválasztva és szögletes zárójelbe zárva. A tömbelem-index egyértelműen meghatározza egy tömb valamely elemének a helyét. A tömbelem-indexet egy változó (tömbnév) után helyezzük el és a tömbelem integrál részét képzi. Az MQL4-ben az indexelés nullával kezdődik. Az olyan indexelés is elfogadható, amikor mindegyik index külön zárójelben van:

143 Egy kétdimenziós tömb mindennapos analógja egy moziterem. A sor száma az első index értéke, és a soron belül levő ülőhely száma a második index, a nézők a tömbelemek, a néző vezetékneve a tömbelem értéke, a mozijegy (a sor és ülőhely meghatározása) egy módszer, amivel hozzáférünk egy tömbelem értékéhez. Tömbdeklaráció és hozzáférés a tömbelemekhez Mielőtt használ egy tömböt egy programban, azt deklarálni kell. Egy tömböt ugyanúgy, mint egy változót deklarálhatunk globális és helyi szinten. Mint az várható volt, a globális tömbelemek értékei elérhetőek az egész programból, a helyi tömbelemek értékei csak abból a függvényből, amiben deklarálva van. Egy tömböt nem deklarálhatnak az ügyfélterminál szintjén, vagyis az ügyfélterminál globális változóit nem gyűjthetik egy tömbbe. A tömbelemek értéki bármilyen típusúak lehetnek. A tömbelemek értékeinek a típusa az, amit a tömbdeklarációnál megadtak. Amikor deklarálunk egy tömböt meg kell adni a tömb elemeinek típusát, a tömb nevét és a dimenziókban lévő elemek számát: A tömbelemekhez csak elemenként férhetünk hozzá, vagyis egy adott pillanatban csak egy összetevőhöz férhetünk hozzá. A tömbösszetevők típusát nem adjuk meg a programban. A tömbösszetevő értéket hozzá lehet rendelni vagy meg lehet változtatni az értékadó operátorral: A tömbelemek értéke az 59. ábrán a következő: - az egydimenziós tömb Mas[4] elem értéke a 34 egész szám; - a kétdimenziós tömb Mas[3,7] elem értéke a 28 egész szám; - a háromdimenziós tömb Mas[5,4,1] elem értéke a 77 egész szám. Megjegyzés: a tömbelem-index minimális értéke 0 (nulla) és a legnagyobb értéke egyel kisebb a megfelelő dimenzió elemeinek a számánál, amit a tömbdeklarációnál megadtunk. Például, a Mas[10][15] tömbben az elem index legkisebb értéke Mas[0,0], és a legnagyobb értéke az elem indexnek Mas[9,14]. A tömbökkel szintén végezhetünk műveleteket a beépített függvények használatával. További információt keress a fejlesztő honlapján (http://docs.mql4.com) vagy a Meta Editor Helpben. Ezek közül a függvények közül néhányat tovább elemzünk. Tömb inicializálás

144 Egy tömböt a megfelelő típus állandói által inicializálhatunk. Egydimenziós és többdimenziós tömböket inicializál az állandóknak az egydimenziós sorozata, amiket vesszők választanak el. A sorozatot kapcsos zárójelekbe zárjuk: int Mas_i[3][4] = 0, 1, 2, 3, 10, 11, 12, 13, 20, 21, 22, 23 ; double Mas_d[2][3] = 0.1, 0.2, -0.3, -10.2, 1.5, 7.0 ; bool Mas_b[5] = false, true, false, true, true Az tömb inicializásakor egy vagy több állandót kihagyhatunk. Ez esetben a számszerű típus megfelelő tömbelemeit nulla inicializálja, a string típusú tömbök elemei a "" string érték inicializálja (idézőjelek tartalom nélkül), egy üres karaktersorozat (nem kell összekeverni egy üres hellyel). A következő program bemutatja a tömbök értékeit, amelyeket néhány érték kihagyásával inicializált egy változó sorozat ( arrayalert.mq4 script ): // // arrayalert.mq4 // The code should be used for educational purpose only. // int start() // Special funct. start() string Mas_s[4] = "a","b",,"d"; // String array int Mas_i[6] = 0,1,2,,4,5 ; // Integer type array Alert(Mas_s[0],Mas_s[1],Mas_s[2],Mas_s[3]); // Displaying Alert(Mas_i[0],Mas_i[1],Mas_i[2],Mas_i[3],Mas_i[4],Mas_i[5]); return; // Exit start() // Ha egy egy-dimenziós tömb mérete nincs megadva, azt az inicializáló változó sorozat mérete alapján meghatározza a szerkesztő. Egy tömböt inicializálhat a beépített ArrayInitialize() függvény is. Minden tömb statikus, vagyis statikus típusú még akkor is, ha az inicializálásnál ezt nyíltan nem jelzik. Ez azt jelenti, hogy minden tömb megőrzi az értékeit azon függvények hívásai között is, amelyekben a tömböt deklarálták ( lásd: A változók típusai). Minden tömb, amit az MQL4-ben használunk két csoportba osztható: felhasználói tömbök (a programozó által létrehozott) és elöre definiált tömbök (tömbök előre definiált nevekkel és adattípusokkal). A felhasználói tömbök méretei és az elemeik értékei attól függenek, hogy a programot hogyan hozták létre, vagyis a programozótól. A felhasználói tömbelemek értékei a programvégrehajtás ideje alatt rögzítettek és csak a számítások befejezése után tudjuk őket megváltoztatni. Azonban az elöre definiált tömbök elemeinek az értékeit nem változtathatjuk meg, a méretük növekszik, amikor a történelmi adatokat frissíti a terminál. Felhasználói tömbök A 'switch' operátor fejezet 18. feladatában megoldottunk egy példát. Tegyük azt bonyolultabbá (növeljük a pontok számát, és 100-ig írjuk ki szavakban) és gyakoroljuk a tömbök használatát. 25. feladat: Hozzunk létre egy programot, amiben a következő feltételeket valósítjuk meg: ha az ár túllép egy bizonyos szintet, bemutat egy üzenetet, amiben a túllépést jelzi, (100 pontig); egyébként tájékoztat, hogy az ár még nem lépte át ezt a szintet. A 25. feladat megoldása, ami egy string típusú tömböt használ a következői: (stringarray.mq4 Expert Advisor ): // // stringarray.mq4 // The code should be used for educational purpose only. //

145 extern double Level=1.3200; // Preset level string Text[101]; // Array declaration // int init() // Special funct. init() // Assigning values Text[1]="one "; Text[15]="fifteen "; Text[2]="two "; Text[16]="sixteen "; Text[3]="three "; Text[17]="seventeen "; Text[4]="four "; Text[18]="eighteen "; Text[5]="five "; Text[19]="nineteen "; Text[6]="six "; Text[20]="twenty "; Text[7]="seven "; Text[30]="thirty "; Text[8]="eight "; Text[40]="forty "; Text[9]="nine "; Text[50]="fifty "; Text[10]="ten "; Text[60]="sixty"; Text[11]="eleven "; Text[70]="seventy "; Text[12]="twelve "; Text[80]="eighty "; Text[13]="thirteen "; Text[90]="ninety"; Text[14]="fourteen "; Text[100]= "hundred"; // Calculating values for(int i=20; i<=90; i=i+10) // Cycle for tens for(int j=1; j<=9; j++) // Cycle for units Text[i+j]=Text[i] + Text[j]; // Calculating value return; // Exit init() // int start() // Special funct. start() int Delta=NormalizeDouble((Bid-Level)/Point,0);// Excess // if (Delta>=0) // Price is not higher than level Alert("Price below level"); // Alert return; // Exit start() // if (Delta<100) // Price higher than 100 Alert("More than hundred points"); // Alert return; // Exit start() // Alert("Plus ",Text[Delta],"pt."); // Displaying return; // Exit start() // A probléma megoldására a Text[] string tömböt használjuk. A program végrehajtása alatt a tömbelemek értékeit nem változhatnak meg. A tömböt globális szinten deklaráljuk (a különleges függvényeken kívül), a kezdeti tömb előkészítés az init()-ben történik. Így a start() függvényben csak a szükséges számításokat végezzük minden ticknél. A Text[] tömbelemek értékeit string állandóként jelöljük ki. A továbbiakban, a ciklusokban kiszámolt értékeket összeadjuk és hozzárendeljük a string sorokat. for (int i = 20; i<=90; i=i+10) // Cycle for tens for (int j=1; j<=9; j++) // Cycle for units Text[i+j] = Text[i] + Text[j]; // Calculating value Ezeket a számításokat könnyen megérthetjük: a indexszel rendelkező tömbelemekhez (kivétel a 10 többszöröseinek indexei) a megfelelő string értéket kiszámoljuk. Figyeld az indexeket a következő sorban: Text[i+j] = Text[i] + Text[j]; // Calculating value Indexként változókat (amely értékei megváltoznak a ciklusban) és kifejezéseket használunk. Az i és j változók értékeitől függően, a program összegezni fogja az értékeket, és ezt az értéket el fogja

146 küldeni a megfelelő Text[] tömbelemekhez, ahol a kiszámított (i+j) eredménye fogja kijelölni a tömbelem értékét. Például, ha a számítás eredményeként i értéke 30 és j értéke7, azon elemek neve, amelyeket össze kell fűzni a Text[30] és Text[7] tömbelem értéke, ezt az eredményt jelöli ki a Text[37]. Bármilyen másik egész számtípusú változót használhatunk tömbelem indexként. Ebben a példában a start() függvényben a Delta változót használjuk tömb indexként - Text[Delta]. A start() különleges függvénynek egyszerű kódja van. A Delta értékétől függő számításokat végzünk. Ha ez kisebb vagy egyenlő mint 100 pont, akkor a megfelelő üzenet mutatása után a start() végrehajtás befejeződik. Ha az érték a meghatározott tartományon belül van, a riasztást a problémafelvetés szerint mutatja. 60. ábra. A kívánt értéket mutatja a stringarray.mq4 EA Figyelj 18. feladat megoldására. Ha ugyanazt a megoldás használtuk volna a 25. feladat megoldására, a 'switch' operátor körülbelül 100 sort tartalmazna - egy sort mindegyik megoldási variációhoz. A programfejlesztés ilyen megközelítése nem megfelelő. Azonfelül, az olyan megoldások kezelhetetlenek ahol több tíz, több száz vagy több ezer változó értékével kell dolgozni. Ilyen esetben a tömbök használata indokolt és nagyon kényelmes. Előre definiált tömbök Az előre definiált tömb egy tömb előre meghatározott névvel (Open, Close, High, Low, Volume és Time), amely tömbök elemei tartalmazzák a történelmi bárok megfelelő jellemzőinek az értékeit. Az adatok, amelyeket az előre definiált tömbök tartalmaznak, nagyon fontos információk és az MQL4 programozásban széles körben használjuk őket. Mindegyik előre definiált tömb egy egy dimenziós tömb, és egy bizonyos bar jellemzőiről tartalmaz történelmi adatot. Mindegyik bárt jellemez a nyitó ár Open[], a záró ár Close[], a maximális ár High[], a minimális ár Low[], a mennyiség (a bárban lévő tickek száma) Volume[] és a nyitási idő Time[]. Például az Open[] előre definiált tömb az ablakban lévő bárok nyitási árairól ad információt: az Open[1] tömbelem értéke az első bár nyitó ára, Open[2] a második bár nyitó ára, stb. ugyanúgy igaz ez a többi bár jellemzőre is. A Zéró bár az aktuális bár, ami még teljesen nem alakult ki. Az ablakban a zéró bár jobbról az utolsó. Bárok (és megfelelő előre definiált tömbindexek) számolását a null bártól indítjuk. A [0] indexszel rendelkező előre definiált tömb elemek értékei azok az értékek, amelyek a zéró bárt jellemzik. Például Open[0] értéke a zéró bár nyitó ára. 61. ábra mutatja a bárok indexelését és a bárjellemzők összegzését

147 61. ábra. Mindegyik bárt az értékek készlete jellemez, amit az arrays-timeseries tartalmaz. A bárokat a zéró bártól számoljuk. A 61. ábrán a zéro bárnak a következő jellemzői vannak: Index Open[] Close[] High[], Low[], Time[] [0] :34 Egy idő után az aktuális bár kialakul és egy új bár fog meg jelenni az ablakban. Most ez az új bár lesz a zéró bár, és a kialakult teljes bár lesz az első ([1]indexszel): 62. ábra. A bárok egy idő után elmozdulnak, míg a számozás nem mozdul el. Most az előre definiált tömb elemeinek az értékei a következők lesznek: Index Open[] Close[] High[], Low[], Time[] [0] :35 [1] :34 A későbbiekben, az ablakban új bárok fognak megjelenni. A formálódó, jobbszélső bár sorszáma mindig nulla lesz, az ettől balra lévő lesz az első bár, a következő - az második, stb. A bár tulajdonságait nem változtathatjuk meg: a példában a bárt 14:34-nél nyitották ki, és a nyitási idő mindig 14:34 fog maradni, a többi paraméter szintén változatlan marad. Azonban ennek a bárnak az indexe az új bárok megjelenése után növekedni fog. Tehát az előre definiált tömbökkel kapcsolatban a legfontosabb szabály a következő:

148 Az előre definiált tömb elemek értékei egy bár megfelelő jellemzői és ezek változatlanok (a zéró bár következő jellemzőit kivéve: Close[0], High[0], Low[0], Volume [0]), a bár index aktuális változása visszatükrözi a bár létrejötte óta eltelt időt. Tudni kell, hogy a bár nyitási időpontját a percek többszörösével számolják, a másodperceket nem vesznek számításba. Más szóval, ha a 14:34 és 14:35 közötti időszakban egy új tick 14:34:07-nél érkezett, akkor a legnagyobb felbontásnál, a perces idősíkon 14:43-nál fog megjelenni. A 15 perces felbontásban a bár nyitási időpontok a 15 perc többszörösei, egy órán belül az első bárt az óra 00 percében, a másodikat a 15. a harmadikat a 30. és a negyediket a 45. percében nyitja. A előre definiált tömbökben történő indexelés jelentőségének megértéséhez oldjunk meg egy egyszerű feladatot: 26. feladat: Keressük meg a minimális és a maximális árat az utolsó n bárban. Figyelem, az ilyen problémák megoldása lehetetlen anélkül, hogy az előre definiált tömböket alkalmaznánk. Annak az Expert Advisornak, amely meghatározza az legnagyobb és a legkisebb árat az előre beállított számú utolsó bár között, a következő lehet a megoldása (extremumprice.mq4): // // extremumprice.mq4 // The code should be used for educational purpose only. // extern int Quant_Bars=30; // Amount of bars // int start() // Special funct. start() int i; // Bar number double Minimum=Bid, // Minimal price Maximum=Bid; // Maximal price for(i=0;i<=quant_bars-1;i++) // From zero (!) to.. //..Quant_Bars-1 (!) if (Low[i]< Minimum) // If < than known Minimum=Low[i]; // it will be min if (High[i]> Maximum) // If > than known Maximum=High[i]; // it will be max Alert("For the last ",Quant_Bars, // Show message " bars Min= ",Minimum," Max= ",Maximum); return; // Exit start() // Az extremumprice.mq4 programban egy egyszerű algoritmust használunk. Az elemezni kívánt bárok számát a Quant_Bars extern változóban adjuk meg. A program elején a vételi árat jelöljük ki a kívánt Minimumnak és Maximumnak. A legnagyobb és legkisebb értékek keresése a ciklusoperátorban történik: for(i=0;i<=quant_bars-1;i++) // From zero (!) to.. //..Quant_Bars-1 (!) if (Low[i]< Minimum) // If < than known Minimum = Low[i]; // it will be min if (High[i]> Maximum) // If > than known Maximum = High[i]; // it will be max Itt megadjuk a Low[i] és High[i] előre definiált tömbelemek feldolgozott index érték tartományát (i integer változó). Figyelj az 1. Kifejezésre és a Feltételre a ciklusoperátor fejlécében:

149 for(i=0;i<=quant_bars-1;i++) // From zero (!) to.. Az első ismétlést a nulla értékű indexszel végezzük. Ez azt jelenti, hogy az első ismétlésben a zéró bár értékeit elemezzük. Így szavatolt, hogy azokat az utolsó ár-értékeket, amik megjelentek az ablakban, szintén számításba vegyük. Az Előre definiált változók rész tartalmazza azt a szabályt, ami szerint minden előre definiált változó értékét, köztük az előre definiált tömböket is, frissíti a terminál a különleges függvények kezdetekor. Ezért az árértékek változatlanok fognak maradni a számítások alatt. Az előre definiált tömb indexének felső határa a feldolgozott bárok számánál egyel kisebb. A példánkban a bárok száma 30. Ez azt jelenti, hogy a legnagyobb indexérték 29. Az előre definiált tömb elemek index értékei 0-tól 29-ig terjednek, vagyis 30 bárt fogunk feldolgozni a ciklusban. Könnyű megérteni a számításokat a ciklusoperátor-törzsben: if (Low[i]< Minimum) // If < than known Minimum = Low[i]; // it will be min if (High[i]> Maximum) // If > than known Maximum = High[i]; // it will be max Ha a Low[i] árfolyamérték (az ismétlés alatt az aktuális indexhez tartozó ár érték) alacsonyabb az eddigi minimális értéknél ez az új érték válik a minimális értékké. Ugyanezzel a módszerrel kiszámoljuk ki a legnagyobb értéket is. A ciklusok végén megkapjuk a Minimum és Maximum változók kívánt értékét. A további programsorokban ezeket az értékeket jelezzük ki. A program végrehajtásával ezt az eredményt kapjuk: 63. ábra. Az extremumprice.mq4 EA végrehajtásának eredménye. Figyeljük meg, hogy az Expert Advisor végtelenül hosszú ideig helyesen működhet, és a program ugyanazokat az index értékeket fogja használni (ebben az esetben 0-tól 29-ig). A null indexszel rendelkező előre definiált tömbelemek értékei változni fognak az új tick megjelenésekor (kivéve az Open[] és Time[] értékeit, mert azok már nem változhatnak a null báron). Néhány esetben akkor kell valamilyen műveletet végrehajtanunk, amikor egy bár teljesen kialakult. Ez például az olyan algoritmusokban fontos, amik a gyertyaelemzésen alapultak. Ilyen esetben csak a teljesen kialakult bárokat vesszük számításba. 27. feladat: Mindegyik bár kezdetén mutassunk egy üzenetet az utolsó n kialakult báron belüli minimális és maximális árról

150 A feladat megoldáshoz meg kell határozni egy új bár kezdetét és észlelni kell egy új ticket a nullbáron. Van erre egy egyszerű és megbízható módszer - figyeljük a nullbár nyitási idejét. A nullbár nyitási ideje az a bárjellemző, ami nem változik a bár kialakulása alatt. Az új tickek, amik egy bár kialakulása alatt jönnek, megváltoztathatják a High[0], Close[0] és Volume[0] értékét. De az Open[0] és Time[0] értékét nem változtathatják meg. Ezért elég az, hogy figyeljük a null bár nyitási idejét, és mindegyik ticknél azt összehasonlítjuk az utoljára ismert nulla bár nyitási idejével. Ha nem találunk egyezést, az azt fogja jelenteni, hogy egy új bár kezdődött (és az előző befejeződött). A newbar.mq4 EA tartalmazza egy új bár észlelésének az algoritmusát, és azt egy felhasználói függvény alakjában valósítja meg: // // newbar.mq4 // The code should be used for educational purpose only. // extern int Quant_Bars=15; // Amount of bars bool New_Bar=false; // Flag of a new bar // int start() // Special funct. start() double Minimum, // Minimal price Maximum; // Maximal price // Fun_New_Bar(); // Function call if (New_Bar==false) // If bar is not new.. return; //..return // int Ind_max =ArrayMaximum(High,Quant_Bars,1);// Bar index of max. price int Ind_min =ArrayMinimum(Low, Quant_Bars,1);// Bar index of min. price Maximum=High[Ind_max]; // Desired max. price Minimum=Low[Ind_min]; // Desired min. price Alert("For the last ",Quant_Bars, // Show message " bars Min= ",Minimum," Max= ",Maximum); return; // Exit start() // void Fun_New_Bar() // Funct. detecting.. //.. a new bar static datetime New_Time=0; // Time of the current bar New_Bar=false; // No new bar if(new_time!=time[0]) // Compare time New_Time=Time[0]; // Now time is so New_Bar=true; // A new bar detected // A New_Bar globális változót használjuk a programban. Ha az értéke 'true', ez azt jelenti, hogy az utoljára ismert tick egy új bár első tickje. Ha a New_Bar érték 'false' az utoljára ismert tick az aktuális nulla báron belül van, vagyis nem a bár első tickje. Flag (zászló) egy változó, aminek az értékét valamely esemény vagy tény határozza meg. Egy programban a flag-ek használata nagyon kényelmes. A flag értékét valahol beállítjuk, és különböző helyekben használhatjuk. Néha olyan algoritmust használunk a programban, amiben egy döntés meghozása különböző flag-ek értékének a kombinációjától függ. A newbar.mq4 Expert Advisorban a New_Bar változót egy flag-ként használjuk. Az értéke közvetlenül egy új bár képződésének tényétől függ. Az új bár észlelését jelző számításokat a Fun_New_Bar() felhasználói függvényben helyeztük el. A függvény első sorában a New_Time statikus változót deklaráljuk (emlékezzünk, a statikus változók nem vesztik el az értékeiket, a függvény végrehajtása után). Mindegyik függvényhívásnál a New_Bar globális változó értékét 'false'-ra állítjuk. Egy új bár észlelését 'if' operator végrehajtása végzi:

151 if(new_time!= Time[0]) // Compare time New_Time = Time[0]; // Now time is so New_Bar = true; // A new bar detected Ha New_Time értéke (az előző munkamenetben kiszámolt) nem egyenlő a Time[0]-val, ez jelenti egy új bár képződésének a tényét. Ezesetben a vezérlés lekerül az 'if' operátor testhez, ahol a null bár új nyitási idejét tároljuk és az új értéket kapja a New_Bar változó (azt is mondhatjuk, hogy a zászlót egy emelkedő pozícióba helyeztük). Miközben ilyen problémákat oldunk meg, fontos számba venni a felhasznált különböző flag-ek sajátosságait. Ebben az esetben a New_Bar sajátossága az, hogy az értéket (flag) még azelőtt kell frissíteni mielőtt az értékét a számításban felhasználnánk (ebben az esetben - a különleges start()függvényben). A New_Bar értékét meghatározó felhasználói függvényt, ezért kell a program elején meghívni, még az első olyan számítás előtt, amiben a New_Bar-t használjuk. A különleges start() függvény felépítése ennek megfelel: felhasználói függvényhívást közvetlenül a változók deklarációja után hajtjuk végre. A kívánt értékek számítása csak akkor szükséges, ha a start()-ot egy olyan tick indítja el, ami egy új bárt kezd. Ezért a start() indítása után azonnal vizsgálja egy új bárképződést, a flag helyzetét (a New_Bar értékét elemzi): if (New_Bar == false) // If bar is not new.. return; //..return Ha az utolsó tick, ami elindította start() végrehajtását, nem alakít egy új bárt, a vezérlés átkerül egy olyan operátorhoz, ami befejezi a start() végrehajtását. És csak ha egy új bár nyílik, akkor kerül a vezérlés tovább a következő sorra (mit a feladatmegoldás igényel). A legnagyobb és legkisebb érték számítása közben használtuk az ArrayMaximum() és ArrayMinimum() beépített függvényeket. Mindkét függvény tömbelem-indexet küld vissza (a legnagyobb és legkisebb értéknek megfelelően) egy meghatározott index tartományból. A problémafelvetés szerint csak a teljesen alakult bárokat kell elemezni, ez az oka, hogy a kiválasztott index intervalluma miért 1 és Quant_Bars - a bárok mennyisége (a zéró bár még nem alakult ki és ezért nem vesszük figyelembe a számítások alatt). Az előre definiált tömbök függvényeivel kapcsolatos további információt találunk a fejlesztő honlapján (http://docs.mql4.com) vagy a MetaEditor Helpben. A 64. ábrán láthatjuk a programvégrehajtás alatt, az előre beállított időintervallumban lévő legnagyobb és legkisebb árak változását: 64. ábra. A newbar.mq4 Expert Advisor végrehajtásának eredmény

152 Gyakorlati programozás MQL4-ben A könyvnek ez a jelenlegi második része a következőt kérdéseket tárgyalja: a kereskedelemi műveletek végrehajtásának a rendje, a kódolás elvei és az egyszerű scriptek, Expert Advisorok és indikátorok használata az MQL4 általános fuggvényeivel. Minden részhez tartozik néhány használatra kész példaprogram, amelyek korlátozottan alkalmazhatók. Az Egy szabályos program című részben bemutatunk egy olyan példát, amit alapként tudsz használni, ha egy olyan egyszerű Expert Advisort akarsz tervezi magadnak amit a valódi kereskedésben használhatsz. Minden, az alábbiakban ismertetett program csak oktatási célra használható és nem minősül kereskedelemi iránymutatásként valódi számlán. A kereskedelmi műveletek programozása Miközben kereskedelmi műveleteket programozol, figyelembe kell venned a dealing center által a megbízások adásával kapcsolatos követelményeket és korlátozásokat, szintén tartsd szem előtt a megbízások végrehajtásának sajátosságait. Ebben a részben szerepel a megbízások végrehajtásának a részletes leírása és sok olyan példát tartalmaz, ahol megmagyarázzuk minden kereskedelmi függvény célját, és bemutatjuk a kereskedelemben való használatukat. Ez a rész tartalmaz néhány kész scriptet, amelyek korlátozottan alkalmazhatók. Egyszerű MQL4 programok Miután a programozó elsajátította a kereskedelmi műveletek programozását, készen áll az egyszerű programok létrehozására. Ez a rész azokkal az általános elvekkel foglalkozik, amelyeket egy egyszerű Expert Advisor és egy egyszerű indikátor kialakításánál meg kell fontolni. Továbbá ez a rész leírja az adatátvitel módját az egyéni indikátorokból egy EA-ba. Ez a fejezet szintén ad néhány példát a kereskedelmi gyakorlatban használható egyszerű programokra. Standard (beépített)függvények Az MQL4 több mint 220 beépített függvényt tartalmaz a technikai indikátorok függvényei nélkül. Ezeket a számukra való tekintettel meglehetősen nehéz volna leírni ebben a könyvben és nehéz volna mindegyik függvényre példát felhozni. Néhány függvényt, ami részletes magyarázatot igényel, már bemutattunk az előző részekben. Ebben a jelenlegi részben a leggyakrabban használt beépített függvényeket és néhány példát találunk arra, hogy hogyan lehet használni őket a programokban. Mindegyik alfejezet végén ismertetjük akategória függvényeinek teljes listáját és röviden leírjuk őket. Egy szabályos program létrehozása Azt a szabályt követjük, hogy miután gyakoroltuk néhány egyszerű MQL4 alkalmazás kódolását, finomítunk a projekten: létrehoz egy kényelmesen használható programot, amit a gyakorlatban is alkalmazhatunk. Néhány esetben az egyszerű programok nem elégítik ki egy kereskedő programozó szükségleteit legalább két okból: 1. Az egyszerű programok korlátozott funkcionalitása teljesen nem látja el a kereskedőt minden szükséges információval és a kereskedéshez szükséges eszközzel ezért az ilyen programok alkalmazása kevésbé hatékony. 2. Az egyszerű programok tökéletlen kódja nehézzé teszi, hogy továbbfejlesszék őket, és hogy növeljék a szolgáltatásaikat. Ebben a jelenlegi részben bemutatjuk egy Expert Advisor lehetséges megvalósítását, amely alap lehet a saját projekted létrehozásához

153 A kereskedelmi műveletek programozása Amikor kereskedelmi műveleteket programozunk, ismernünk kell azokat a követelményeket és korlátozásokat, amelyek egy szabályos megbízást jellemeznek, szintén ismerni kell a kereskedelmi megbízás végrehajtás technológiájának sajátos jegyeit. Ez a rész a kereskedés végrehajtásának részletes leírását nyújtja, és sok olyan példát tartalmaz, amelyek megmagyarázzák a kereskedelmi függvények célját és alaki követelményeit. Ez a rész tartalmaz néhány kész, korlátozottan alkalmazható scriptet. A kereskedés gyakorlati végrehajtása A kereskedő vagy az MQL4 program csak akkor hajthatja végre a kereskedést, ha kereskedési kérését bejegyzik a kereskedelmi szerveren. A közvetítő a kereskedelmi szerver és egy program között az ügyfélterminál. A helytelen kéréseket viszza fogja utasítani az ügyfélterminál, ezért betekintést kell nyernünk a kereskedés általános rendjébe. A megbízások jellemzői és a kereskedés végrehajtási A kereskedelmi megbízások kezeléséhez kereskedési utasításokat használunk. Ezekben az utasításokban többszörös paramétereket kell megadnunk, ezek egy részét az aktuális ár és a kereskedelem iránya határozza meg, más része a kereskedelem szimbólumától függ. Azokat a megbízásokat, amelyeket a kereskedelmi szerverre küld az ügyfélterminál, ott valós idejű módban ellenőrizni fogják, hogy megfelelnek-e az aktuális piaci helyzetnek és a számlaegyenlegnek. Ezért meg kell ismernünk a kereskedés végrehajtásának szabályait. Megbízások nyitása és elhelyezése A legfontosabb kereskedői függvény az OrderSend(). Ez ez a függvény, amit arra használunk, hogy piaci megbízás nyitási és függőben lévő megbízás elhelyezési kéréseket küldjön a kereskedelmi szervernek. Azonnal megadhatjuk a StopLoss és TakeProfit szükséges értékeit. Ezeknek a paramétereknek a helytelen értékei hibás működéshez vezethetnek. Fontos, hogy ezeket a hibákat megfelelően feldolgozzuk. A MarketInfo() függvény lehetővé teszi, hogy ezeket a hibákat minimalizáljuk. Megbízások zárása és törlése. Az OrderSelect() függvény A piaci megbízások az OrderClose() függvénnyel zárhatjuk le, és a függőben levő megbízásokat az OrderDelete() fügvénnyel törölhetjük. Amikor egy megbízás záró vagy törlő kérést küldünk, meg kell adni ennek a megbízásnak a jegyszámát (ticket). A szükséges magbízást az OrderSelect() függvénnyel fogjuk kiválasztani. Azonkívül, ha két ellentétes megbízásunk van egy szimbólumon, egyidejűleg be tudjuk zárni őket, egyiket a másik által az OrderCloseBy() függvénnyel. Ha ilyen kereskedelmi müveletet hajtunk végre, az egyik spreadet meg fogjuk spórolni. A megbízások módosítása A TakeProfit és StopLoss szinteket az OrderModify() függvénnyel módosíthatjuk. A függőben levő megbízások esetén a kért nyitó ár szintén megváltoztatható. Nem módosítható azonban a függőben levő megbízás mérete. A piac és függőben levő megbízások módosítására szintén vonatkoznak bizonyos követelmények, amelyeket be kell tartani. Nagyon ajánlott, hogy ha végrehajtunk egy kereskedelmi kérést értékeljük annak az eredményét és kezeljük a hibákat

154 A kereskedés gyakorlati végrehajtása Minden számítás és minden más esemény, ami egy alkalmazás végrehajtása során történik, a végrehajtás helyszíne alapján két csoportba osztható: azokra, amelyeket a felhasználó PC-jében hajtanak végre, és azokra, amelyek végrehajtása a szerveren történik. Jelentős mennyiségű számítás történik a felhasználó oldalán. Ez a csoport tartalmazza az alkalmazási programok végrehajtását. A kereskedés a második csoporthoz tartozik. A kereskedés végrehajtása során adatcsere történik a szerverrel. A kereskedéssel kapcsolatban a következő meghatározásokat használjuk: Market order. Piaci megbízás - egy vételi vagy eladási megbízás végrehajtása az aktuális áron. A megbízást annak lezárásáig látjuk a szimbólumablakban. Pending order. Függőben levő megbízás egy olyan vételi vagy eladási megbízás, ami az előre beállított árszint elérésekor teljesül. A függőben levő megbízást addig látjuk a szimbólumablakban, amíg nem teljesül vagy nem töröljük. Trade Request. Kereskedelmi kérelem- egy olyan parancs, ami kéri a megbízás végrehajtását. Trade. Kereskedés a megbízás nyitása, zárása vagy módosítása. A kereskedés folyamatábrája Három összetevője van a kereskedés végrehajtásának: az alkalmazási program, az ügyfélterminál és a szerver (lásd a 65. ábrát). A kereskedelmi kérés az alkalmazási programban keletkezik (mint ahogy fent megemlítettük, az alkalmazási programokat csak a felhasználó PC-jében hajthatjuk végre; alkalmazási programokat nem telepíthetünk a szerverre). A kérést, amit a program létrehozott, átadja az ügyfélterminálnak, ami a kérést tovább küldi a szervernek. A szerver fogadja a kérést és dönt a kérés végrehajtásáról vagy az elutasításról. A döntés eredményével kapcsolatos információt a szerver visszaküldi az ügyfélterminálnak, ahonnan visszakerül a programhoz. Kereskedelmi kérelem 65. ábra. A kereskedelmi kérelem diagramja. A kereskedelmi kérelem származhat a kereskedőtől vagy egy programtól. A kereskedőnek lehetősége van rá, hogy kereskedelmi kérelmet küldjön az ügyfélterminál Megbízás paneljén keresztül (lásd az ügyfélterminál leírását). A kérelmeket a program a kereskedés végrehajtására vonatkozó algoritmus alapján adja. Sehol máshol (sem az ügyfélterminálban sem a szerveren) kereskedelmi kérések spontán nem keletkeznek. Programjellemezők Az algoritmustól függően egy program különböző kéréseket indíthat nyitás, zárás vagy a függőben levő megbízások módosítása. A következő kereskedelmi függvényeket használjuk egy programban a kereskedelmi kérelmekhez: OrderSend() megbízás nyitása és függőben lévő megbízás; OrderClose() és OrderCloseBy() megbízás lezárása; OrderDelete() - függőben levő megbízás törlése; OrderModify() - piaci és függőben levő megbízás módosítása. A fenti kereskedelmi függvényeket csak Expert Advisorban és scriptekben használhatjuk; ezeknek a függvényeknek az indikátorokban levő használata tilos (lásd: 2. táblázat). Van több, a kereskedelemmel kapcsolatos függvény is (lásd a Meta Editor súgót és e könyv Kereskedelmi

155 függvények részét). Ezek végrehajtása az ügyfélterminál információs környezetével történő együttműködésre szolgál, és nem végződik azzal, hogy kéréseket küldjenek a szerverre. Az ügyfélterminál jellemzői A kérés, amit egy kereskedelmi függvény végrehajtásának eredményeképpen küld a program feldolgozásra az ügyfélterminálhoz kerül. Az ügyfélterminál elemzi a kérés tartalmat és a következő két művelet közül az egyiket végrehajtja: a kérést végrehajtásra elküldi a szervernek, vagy visszautasítja a kérést és semmit nem küld a szervernek. Az ügyfélterminál csak a korrekt kéréseket küldi el a szervernek. Például, ha a program a kódja alapján, egy nem létező árnál történő megbízásra ad kérést az ügyfélterminál nem fogja ezt a kérést elküldeni a szervernek. Ha a program helyes kéréseket küld (a megbízások nyitása és zárása a legújabb ismert árnál történik és a megbízás a dealing center által megszabott tartományon belül van), akkor ezt a kérést tovább fogja küldeni a szervernek. Az ügyfélterminálban a kereskedés végrehajtása csak egy szálon futhat. Ez azt jelenti, hogy az ügyfélterminál egyidejűleg csak egy kéréssel foglalkozik. Ha különböző Expert Advisorok vagy scriptek az ügyfélterminállal kereskednek és valamely program egy kereskedelmi kérést küldött az ügyfélterminálnak, a többi Expert Advisor vagy script kereskedelmi kéréseit vissza fogja utasítani, amíg az ügyfélterminál befejezi aktuális kérés végrehajtását, vagyis amíg a kereskedelmi szál felszabadul. Szerverjellemezők A kereskedelmi eseményekkel, számlákkal kapcsolatos információk (nyíló, záró, módosító megbízások) a szerveren biztonságosa tárolva vannak és ezeknek az adatoknak a prioritása magasabb, mint az ügyfélterminálban lévő adatoké. Egy kérést, amit a szerver kap vagy végrehajt vagy visszautasít. Ha a kereskedelmi kérés végre lett hajtva (a kereskedés megtörtént), a szerver minden szükséges adat módosítást elvégzi. Ha a kereskedelmi kérést visszautasítja, a szerver nem változtat meg semmilyen adatot. A szerver kivétel nélkül minden döntéssel kapcsolatos információt átad az ügyfélterminálnak, hogy az szinkronizálja az eseményeket. Az a kereskedelmi kérés ami egy programvégrehajtás eredménye, és az a kereskedelmi kérés amit kézzel adott kereskedő a szerver szempontjából ugyanaz, a szerver nem tesz különbséget a kérések feldolgozása során. Lehetséges, hogy a szerveroldal megtiltsa az Expert Advisornak, hogy az ügyfélterminállal kereskedjen. Ez akkor szükséges, ha a program végrehajtása konfliktusokat okoz. Például, ha egy helytelen algoritmus futtatása azzal végződik, hogy a program folytonosan nyitás és zárási megbízásokat ad, nagyon kicsi időközönként módosítja a megbízásokat (például minden ticknél), vagy ha a függőben levő megbízások nyitási, törlési vagy módosítási megbízásai túl gyakoriak. A kereskedés folyamata A kereskedelem végrehajtása interaktív és valós idejű módban valósul meg. A diagram (66. ábra) tartalmaz minden eseményt, ami a kereskedés végrehajtása alatt történik. 66. ábra. Az események láncolata a kereskedésben. 0. esemény: A programot a t0 pillanatban elindítjuk

156 1. esemény: A t1 pillanatban a program egy kereskedelmi függvény végrehajtása következtében létrehoz egy kereskedelmi kérést. A kereskedelmi kérést átadja az ügyfélterminálnak. Abban a pillanatban a program átadja a vezérlést az ügyfélterminálnak és a program végrehajtása leáll (a piros pont a diagramban). 2. esemény:az ügyfélterminál megkapta a vezérlést és a kérés tartalmával kapcsolatos információt. A t2 - t3 időszakban az ügyfélterminál elemzi a kereskedelmi kérés tartalmát és dönt a további eseményekről. 3. esemény:az ügyfélterminál végrehajtja a meghozott döntését (a két alternatíva közül az egyiket). 1. alternatíva: Ha a kereskedelmi kérés, ami egy kereskedő függvény eredménye idejétmúlt vagy hibás, a vezérlés visszakerül a programba. Ebben az esetben a következő esemény a 4. esemény lesz (ez megtörténhet, ha például a program elküldött egy nyitási megbízást, ám annak a fedezet igénye meghaladja a rendelkezésre álló tőkét). 4. esemény: A program visszakapta vezérlést (t4pillanat, zöld pont) és végrehajtást folytatja attól a helytől, ahonnan a kérést korábban elindította. Ebben a pillanatban a program megkapta az információt arról, hogy a kereskedelmi megbízást nem hajtották végre. Tudomást szerezhet annak az okáról, hogy a kereskedelmi kérést miért nem hajtották végre azáltal, hogy elemezi a visszaküldött hibakódot. Lentebb be fogjuk mutatni, hogy hogyan teszi ezt. Itt csak azt kell felismerni, hogy nem minden kereskedelmi kérés végződik végrehajtással. Ebben az esetben a program egy hibás kérést küldött, ezért az ügyfélterminál visszautasította ezt a kérést, és visszaküldte a vezérlést a programba. Ebben az esetben nincs kapcsolatfelvétel a szerverrel. A t1- t2 - t3 - t4 közötti időintervallumok elhanyagolhatóan rövidek és nem nagyobbak összességükben néhány ms-nál. 2. alternatíva: Ha a program egy helyes kereskedelmi kérést indított, az ügyfélterminál ezt a kérést továbbküldi a szervernek; a következő esemény az 5. esemény lesz (t5) pillanat - a szerver meg fogja kapni a kereskedelmi kérést. A kapcsolattartás az ügyfélterminál és a szerver között az Interneten keresztül történik, ezért a kereskedelmi kérés eljutási ideje a szerverre (t3 - t5 időintervallum) teljes egészészében a kapcsolat minőségétől függ. Egy jó minőségű kapcsolat esetén ez az idő hozzávetőleg 5-10 ms lehet, míg egy rossz kapcsolatnál ezt másodpercekben mérhetjük. 5. esemény: t5 pillanatban a szerver megkapta a kereskedelmi kérést. A szerver visszautasíthatja vagy végrehajthatja ezt a kérést. A döntés a végrehajtásról vagy visszautasításról a szerveroldalon egy bizonyos időszakon belül megszületik (t6 pillanat). A t5-t6 időintervallum eltarthat néhány milliszekundumtól több tucat másodpercig, a helyzettől függően. Abban az esetben, ha a szerver automatizált módban működik, gyors mozgások nincsenek a piacon és a többi kereskedő nem nagyon aktív a kereskedelmi kérést végrehajthatják vagy visszautasíthatják pár milliszekundumon belül. Más esetben, ha a szerver a kereskedők magas aktivitása miatt túlterhelt, vagy ha a végrehajtó vagy visszautasító döntés egy bróker hozza meg, a döntéshozatali idő több tucat másodperc is lehet. 6. esemény: Ha jelentős változások nincsenek az adott időintervallumon belül a piacon, vagyis attól a pillanattól, hogy a program meghozta a döntést a kereskedelmi kérésről (t1) addig a pillanatig, amíg a szerver feldolgozta a döntést (t6), a kereskedelmi kérést végre fogják hajtani. Ha a szimbólum ára ezen az intervallumon belül változott, vagy a nyitási megbízás tőkeigénye meghaladja a számla szabad tőkéjét, vagy a más akadályok történnek, akkor a szerver úgy dönt, hogy visszautasítja a kereskedelmi kérést. Gyakori, hogy a szerver a kereskedelmi kéréseket elutasítja (annak ellenére, hogy az ügyfélterminál már ellenőrizte őket). Általában a legtöbb kereskedelmi kérést, amit kézbesítenek a szervernek, a szerver végrehajtásra elfogadja. Mindazonáltal néhány esetben egy kérést visszautasíthatnak, tehát az alkalmazási programodat úgy kell kódolni, hogy ilyen helyzetekben is megfelelően működjön. Bármi is a szerver döntése (a kereskedelmi kérés végrehajtása/visszautasítása a 6. esemény során) az ezzel kapcsolatos információt elküldi a szerver arra az ügyfélterminálra, ami kézbesítette a kérést. 7. esemény:az ügyfélterminál megkapta a szerver válaszát. A szerverválasz ugyanazt az utat járja e az Interneten keresztül mint a kérés amit kézbesítettek a szervernek; tehát az idő, ami a szerverválasz fogadásáig eltelik a kapcsolatminőségtől függ. A módosítások alapján, amiket a szerver végzett az ügyfélterminál vissza fogja tükrözni a megfelelő változásokat. Például, ha a szerver egy kereskedelmi kérés végrehajtása során bezár vagy kinyit egy megbízást, az ügyfélterminál megjeleníti ennek az eseménynek az eredményét a szimbólumablakban és a Terminal ablakban (a Kereskedés és Számlatörténet fülek). Ha a szerver visszautasította a kereskedelmi kérést, változások nem fognak történni az ügyfélterminál egyik ablakában sem. 8. esemény:az ügyfélterminál befejezte a változások megjelenítését és átadja a vezérlést a programnak. 9. esemény: A program megkapta a vezérlést és továbbra is működhet

157 Kérlek, jegyezd meg: Attól a pillanattól kezdve, mikor a program elküld egy kereskedelmi kérést (és egyidejűleg átadja a vezérlést) az ügyfél terminálnak, addig a pillanatig, míg ez az irányítást visszaküldik a programnak az utóbbi várakozó módban van. Műveleteket nem végez a program ez alatt az időszak alatt. A vezérlést visszaküldik a programnak, annak a függvénynek a hívás végrehajtás szabálya szerint, ami indította a kereskedelmi kérést. Ha a kereskedelmi kérés helytelen a program nincs sokáig a várakozó üzemmódban (a t1- t4 időköz). Azonban, ha a kereskedelmi kérést az ügyfélterminál jóváhagyja és elküldi azt a szervernek, a program várakozási időszak hossza (t1-t9) más lesz és a kapcsolatminőségtől valamint a szerver döntéshozatali idejétől függően - néhány milliszekundumtól több percig is tarthat. Amint a program visszakapja a vezérlést, továbbra is működhet. A működő program elemezheti az utolsó hiba kódját, amit visszaküldött az ügyfélterminál, és tudomást szerezz arról, hogy a kereskedelmi kérését végrehajtották vagy vissza utasították. Konfliktusok a kereskedelmekben hiba Amikor tárgyaltuk az ügyfélterminál jellemzőit megemlítettük azt, hogy az ügyfélterminál egy időben csak egy kérést hajthat végre. Vizsgáljuk meg, hogy milyen események fognak zajlani, ha több kérést, -amik különböző programoktól származnak- fognak küldeni az ügyfélterminálra. 67. ábra. Különböző programokból származó kérések konfliktusba kerülnek az ügyfélterminálban. 67. ábrán azt az esetet láthatjuk, amikor két Expert Advisort indítanak el egyidejűleg az ügyfélterminálon. Az EA1 a t1pillanatban indított egy kereskedelmi kérést és a t2 pillanatban átadta azt az ügyfélterminálnak. Az EA2 szintén létrehozott egy kérést és továbbította az ügyfélterminálnak, akkor, amikor az ügyfélterminál az első kérést vizsgálja (t2 - t3 időpont). Ebben a helyzetben, az ügyfélterminál nem fogadja el az EA2 kérését, és azt vissza fogja utasítani, vissza fogja küldeni a vezérlést az EA2-nek. Jegyezd meg, azt ebben az esetben a kérést nem azért utasítja vissza az ügyfélterminál, mert a kérés helytelen, hanem azért mert a terminál a másik kérés feldolgozásával van elfoglalt. Az EA2 továbbra is működni fog. Elemezheti azt a hibakódot, ami jelzi az okot, hogy a kérést miért utasították vissza, (ez esetben ez a hibakód a 146). Az EA2 (általános esetben egy vagy több kereskedő programunk van) átadja a kérését az ügyfélterminálnak a t1 - t4 időidőszakon belül, azután ezt a kérést vissza fogják utasítani (a rózsaszín terület eseményeinek a csoportja). Az ügyfélterminál a t4 (zöld pont) pillanatban szabaddá válik. Ettől a pillanattól kezdve, az EA2 sikeresen továbbíthatja a kérését az ügyfélterminálnak (a zöld területen lévő esemény csoport). Ezt a kérést meg fogja kapni és meg fogja vizsgálni az ügyfélterminál, ami végül visszautasíthat a helytelen volta miatt, vagy a kérést tovább küldheti a szervernek. Ha az EA1által létrehozott kereskedelmi kérésről, az ügyfélterminál úgy dönt, hogy az helyes, ezt a kérést a t3 pillanatban el fogja küldeni az ügyfélterminál a szerverre. Ebben az esetben az ügyfélterminál várakozó módra kapcsol és nem fogad semmilyen másik kereskedelmi kérést. Az ügyfélterminál csak t9 pillanatban fog szabaddá válni, és ettől az időponttól fogad további kéréseket. A 2. alternatíva szerint az ügyfélterminál nem vizsgálja a kereskedelmi kéréseket a t1 - t9 időszakon belül. Ha ezen az időszakon

158 belül, bármilyen program az ügyfélterminálhoz kereskedelmi kérést küld, az ügyfélterminál vissza fogja utasítani ezt a kérést és vissza fogja adni a vezérlést a programnak (a t6 - t7 időszak esemény csoportja) (rózsaszín terület). A program, amint megkapta a vezérlést, folytatja a műveleteit és elemezi a hibakódot, tudomást szerezhet arról az okról, amiért a kérést visszautasították, (ebben az esetben ez a 146. hiba). A t9 pillanattól kezdve az ügyfélterminál, teljesen szabad lesz és fogadhat bármilyen másik kereskedelmi kérést. Az EA2 sikeresen továbbítani tudja a kereskedelmi kérését az ügyfélterminálnak a t9 pillanatot követő időszakban. Az ügyfélterminál attól függően, hogy ez a kérés helyes vagy nem, a kérést továbbítja a szerverre vagy vissza fogja utasítani. Az olyan hibák az elemzését, amik a kereskedelem végrehajtása során történnek, később részletesen tárgyaljuk

159 A megbízások jellemzői és a kereskedés végrehajtás szabályai Mielőtt elkezdjük a kereskedelmi függvényeket leírni, ismernünk kell, hogy milyen paraméterek jellemzik a piaci árakat, mik a megbízások típusai, és melyek a kereskedés szabályai. A szimbólumok jellemzői Először is ismernünk kell, hogy milyen elv szerint alakítják ki a brókercégek az áraikat. Ez az elv abban áll, hogy a bróker felajánl a kereskedőnek egy kettős árat amely árakon a kereskedő kereskedhet. Two-way quote (kettős ár, kétirányú ár) Két összefüggő piaci ár, amit a bróker felajánl valamely szimbólum pillanatnyi vételi és eladási áraként. Bid a bróker által felajánlott két összefüggő ár közül az alacsonyabb ár. Ask a bróker által felajánlott két összefüggő ár közül a magasabb ár. A függelékben található egy olyan összefoglaló táblázat, ami részletezi a kettős árak alkalmazását a megbízások nyitásakor, zárásakor és módosításakor, valamint a korlátozások meghatározásakor. Point valamely szimbólum árának egysége (a lehetséges legkisebb árváltozás, az ár utolsó számjegyének értéke). Spread a kettős ár magasabb és alacsonyabb értéke közötti különbség. Általában a spread egy rögzített érték. A MetaTrader 4 szimbólumablakban látható charton a Bid értékének változását követhetjük: 68. ábra: egy szokásos szimbólumábra (chart). 68. ábrán egy olyan szimbólumablakot láthatunk, ahol figyelemmel követhetjük a kettős árajánlat változásait, - az aktuális Bid árajánlat a fekete sor (1.3005) és az aktuális Ask árajánlat a piros sor (1.3007). Könnyen észrevehetjük, hogy ebben az esetben a bróker ajánlata 2 pont spreadet tartalmaz. Az Ask ár történelmi változását nem mutatják be az ábrán, de ezt könnyen ki lehet számolni bármely pillanatban. Megbízástípusok és azok jellemzői Összességében hat megbízástípus van: két fajta piaci megbízás és négy fajta függőben levő megbízás. Buy - vétel, egy szimbólum piaci áron történő vételére szóló megbízás. Sell - eladás, egy szimbólum piaci áron történő eladására szóló megbízás

160 Buy Limit - egy függőben levő megbízás, egy olyan vételi megbízás, ami alacsonyabb az aktuális árnál. A megbízást akkor fogják végrehajtani (piacira módosított Buy megbízással) ha az Ask ár a függőben levő megbízásban meghatározott értékre csökken. Sell Limit- egy függőben levő megbízás, egy olyan eladási megbízás, ami magasabb, mint az aktuális. A megbízást akkor fogják végrehajtani (piacira módosított Sell megbízással) ha a Bid árajánlat a meghatározott értékre emelkedik. Buy Stop - egy függőben levő megbízás, egy olyan vételi megbízás, ami magasabb az aktuális árnál. A megbízást akkor fogják végrehajtani (piacira módosított Buy megbízással) ha az Ask ár a függőben levő megbízásban meghatározott értékre emelkedik. Sell Stop - egy függőben levő megbízás, egy olyan eladási megbízás, ami alacsonyabb, mint az aktuális. A megbízást akkor fogják végrehajtani (piacira módosított Sell megbízással) ha a Bid árajánlat a meghatározott értékre csökken. Lot a megbízás összegének nagysága. StopLoss- egy stop megbízás; ez egy olyan ár, amit előre beállított a kereskedő, aminél a megbízása lezárul, ha az szimbólum ármozgása veszteséges irányba fordul. Take Profit - egy stop megbízás; ez egy olyan ár, amit előre beállított a kereskedő, aminél a megbízás lezárul, ha a szimbólum ára a nyereséges irányban eléri azt. Kereskedelmi követelmények és korlátozások Azért, hogy az alkalmazási programjaid korrekt kereskedelmi kéréseket küldjenek (Expert Advisorok és scriptek), tekintetbe kell venned a létező követelményeket és korlátozásokat. Vizsgáljuk meg őket részletesen! Minden kereskedelmet korrekt árakon hajtanak végre. Mindegyik végrehajtás a kétirányú ár megfelelő értéke alapján történik. A fenti szabály minden piaci szereplőre vonatkozó szabály, és azt nem tudják megváltoztatni a kereskedő platform fejlesztői, és a bróker és kereskedő közti megegyezés alapján sem változtatható meg. Például egy megbízást csak az aktuális piaci árnál nyithatnak, és semmilyen másik árnál nem. A korrekt ár különböző kereskedelmi helyzetekben való számítási eljárását lent tárgyaljuk. Miközben a korrekt árakat kiszámítjuk, meg kell fontolni a szolgáltató (dealing center) korlátozásait. Ezek a korlátozások tartalmazzák a minimális távolságot és a befagyott zónát. Ezek a korlátozások azt jelentik, hogy a brókernek szüksége van némi időre, hogy az új megbízások (nyitó, záró, módosító és stop ajánlatok) előkészületeit megtegye. A dealing center korlátozza a minimális megengedhető különbséget a piaci ár és a különböző stop megbízások árai között, a különbséget a piaci ár és függőben levő megbízás ára között, és korlátozza a különbséget függőben levő megbízások kért ára és ezen megbízások stop árai között. Például ez azt jelenti, hogy egy piaci megbízás csak akkor elfogadható, ha az abban foglalt stop megbízások távolabb vannak az aktuális piaci ártól az előírt minimális távolságnál. Egy kereskedelmi kérést, ami olyan stop árakat tartalmaz, amik közelebb vannak a piaci árhoz mint a minimális távolság, az ügyfélterminál helytelennek fog tekinteni. A különböző dealing centerek különböző, az adott dealing centerre jellemző korlátozásokat hozhatnak a megengedett távolságra. Általában 1 és 15 pont közötti az ilyen tartományok értéke. A legáltalánosabban használt szimbólumoknál (EUR/USD, GBP/USD, EUR/CHF,, stb.), ez a távolság a legtöbb dealing centerben 3-5 pontot. Különböző szimbólumoknak szintén különböző minimális megengedett távolságaik lehetnek. Például ez az érték az aranynál pont lehet. A bármilyen szimbólum minimális távolság értékét bármikor megváltoztathatja a bróker (ez általában megelőzi a fontos kereskedelmi hírek közvetítését). A maximális távolságra nincsenek korlátozások. A befagyott zóna korlátozza azon stop megbízások és függőben lévő megbízások módosításának lehetőséget, amik ebben a zónában vannak. Például, ha a piaci ár , és függőben levő nyitási megbízásod van nél, és a bróker 10 pontos befagyott zónát határozott meg, a függőben levő

161 megbízás a befagyott zónában van, vagyis nem tudod törölni és nem tudod módosítani azt. Egy nyugodt piacnál a brókerek általában nem állítanak be befagyott zónát, vagyis ez 0. Azonban a fontos hírek előtt vagy magas volatilitású piacon a bróker beállíthatja a befagyott zóna egy bizonyos értékét. Különböző helyzetekben a különböző brókereknél ez az érték az alapvető szimbólumokon 1-től 30 pontig terjedhet, és magasabb értékű lehet más szimbólumoknál. A brókercég bármikor meg tudja változtatni a befagyott zóna értéket a saját belátása szerint. Az árszinteknek azon korlátozását, amik korlátozzák a minimális távolság értékeit és a befagyott zónát a korrekt árak alapján számolják ki. Megbízások nyitása/zárása Kereskedelmi megbízás egy adott szimbólum vétele/eladása az aktuális piaci árakon (lásd: Követelmények és korlátozások a kereskedelemben). A megbízás nyitásához az OrderSend() függvényt, bezárásához az OrderClose() függvényt használjuk. A Buy megbízás korrekt ára a legújabb ismert piaci Ask ár. A Sell megbízás korrekt ára a legújabb ismert piaci Bid ár. A stop megbízások pozíciójával kapcsolatos korlátozás a megbízás nyitásakor érvényes korrekt záróártól számolandó. A StopLoss és Take Profit megbízást nem tudjuk a piaci árhoz közelebb tenni a mint a megengedett minimális távolság. Például a minimális távolság EURUSD-n 5 pont. Sell megbízást nyitunk Bid= en. Az az ár, amelyen ezt a Sell megbízást a nyitás pillanatában le tudnánk zárni a kettős ár alapján Ask= A következő stop szintek lesznek a legközelebbiek, amelyeket az aktuális pillanatban megadhatunk, (lásd a 69. ábrát és Követelmények és korlátozások a kereskedelemben): Stop Loss = Ask + minimális távolság = = , és Take Profit = Ask - minimális távolság = = ábra. A Sell megbízás piaci árhoz legközelibb Stop szintjei. Ha olyan Sell, Bid= árú nyitási megbízást adsz, ahol az előre beállított stop szintek közelebb vannak, mint a fenti értékek (SL= és ТР=1.2984), a kereskedelmi kérést vissza fogja utasítani az ügyfélterminál. Azonkívül figyelembe kell venni az árcsúszások (slipage) lehetőségét a megbízások küldése alatt, amikor a megbízás nyitása egy olyan áron történik, ami eltér a kereskedelmi kérésben megadott értéktől. Ha ennél a kérésnél az előre beállított stop szintek a lehető legközelebb vannak a beállított nyitó árhoz, ezt a kérést szintén vissza fogja utasítani az ügyfélterminál, mert ebben az esetben, a kérés nem

162 tartja be a nyitási ár és a stop szintek közül az egyiknek szükséges minimális távolságát. Ez az oka annak, hogy nem ajánlott olyan megbízások nyitása, ahol a stop értékek a nyitási árhoz nagyon közeliek. Éppen ellenkezőleg, ajánlott egy kevés szabad mozgást hagyni, vagyis olyan stop értékeket megadni, amelyek 1-2 ponttal távolabb vannak a nyitó ártól számított minimum távolsági értékénél. A megbízások lezárhatók a kereskedő vagy program által indított kereskedelmi kéréssel, vagy valamely előre beállított stop árszint elérésekor automatikusan lezárulnak. Egy Buy megbízás korrekt zárási ára a legújabb ismert Bid piaci ár. Egy Sell megbízás korrekt zárási ára a legújabb ismert Ask piaci ár. Ha Sell megbízást (69. ábra) az aktuális pillanatban bezárjuk, a záró ár Ask= lesz, vagyis a veszteség 2 pont. Ha továbbra is nyitva hagyjuk a megbízást és az ár leesik Ask= re a megbízás 3 pont profittal bezárul annál az árnál. Ha a megbízás nyitva van és a piaci ár növekszik, Ask= értékénél a megbízás 7 pont veszteséggel lezárul annál az árnál. Ha az alkalmazás egy olyan kérést küld, hogy nyisson vagy zárjon egy megbízást az ügyfélterminál egy olyan árnál, ami nem egyezik meg a legújabb ismert piaci árral, az a kérést vissza fogja utasítani. A megbízások zárásával kapcsolatos korlátozásokat, a megbízás zárására vonatkozó korrekt piaci ártól számoljuk. A megbízást nem tudjuk bezárni, ha a StopLoss vagy a Take Profit a piaci ártól számított befagyott zónában van. Például, a 69. ábrán látható megbízást csak akkor lehet lezárni, ha a brókerek által beállított fagyott zóna értéket 4 pont vagy kevesebb a zárás pillanatában. Ebben az esetben ennek a megbízásnak a nyitási ára nem számít. A fagyott zónát a piaci ár alapján számoljuk. Ha ez = 4, a fagyott zóna felső határa = = , míg a fagyott zóna alsó határa = = Ezek szerint a feltételek szerint, stop megbízás nincs a fagyott zónában, tehát a megbízást le lehet zárni, ha a kereskedő (vagy egy program) egy megfelelő kérést küld a szervernek. Ha a bróker az aktuális pillanatban a fagyott zóna értékét 5-re állítja, a határai és Ebben az esetben mindkét stop megbízás a fagyott zónában található és bele esik a bróker által meghatározott korlátozásba, ezért a megbízást nem lehet lezárni a kereskedő kezdeményezésére vagy a kereskedő program kérésére. Ebben a példában mindkét stop megbízás a fagyott zónába esik. Általános esetben, egy megbízást nem lehet lezárni az ügyfélterminál (a program vagy a kereskedő) kezdeményezésére, ha ennek a megbízásnak legalább egy stop szintje a fagyott zónában van. Ha egyidejűleg két nyitott, egy Buy és egy Sell megbízásunk van azonos szimbólumon, kétféle módon lehet bezárni őket: be lehet zárni őket egyenként az OrderClose() függvény alkalmazásával,vagy be tudjuk zárni őket egyszerre az OrderCloseBy() használatával. A pénz megtakarítás tekintetében a második út a kívánatosabb, mert meg lehet vele takarítani az egyik bezárandó megbízás spreadjét. A kereskedelmi függvények használatát a később részletesen tárgyalni fogjuk ebben a könyvben. A függőben levő megbízások elhelyezése és törlése Egy függőben levő megbízás egy olyan megbízás, amelynek az ára különbözik az aktuális piaci ártól. Függőben levő megbízást az OrderSend() függvénnyel adhatunk. A függőben levő megbízásokat az OrderDelete() függvénnyel törölhetjük. A függőben levő Sell Limit és Buy Stop megbízásokat az aktuális piaci árnál magasabbra tesszük, a Buy Limit és Sell Stop megbízásokat pedig az aktuális piaci árnál alacsonyabbra. A függőben levő megbízás pozíciójával kapcsolatos korlátozást, a korrekt piaci ár és a függőben lévő megbízás nyitó ára alapján számoljuk ki

163 Függőben lévő Buy Limit, Buy Stop, Sell Limit és Sell Stop megbízásokat nem tudunk az aktuális piaci árhoz a minimális távolságnál közelebb tenni. Például, hogy kiszámítsuk a Buy Stop megbízás minimális megengedett árát, hozzá kell adnia a minimális távolság értékét a legújabb ismert Ask árhoz. Ha a Stop Level= 5, akkor a minimális megengedett ár ahová a függőben levő Buy Stop megbízást adhatjuk = lesz (70. ábra). Ez azt jelenti, hogy a Buy Stop megbízást az aktuális pillanatban ra vagy magasabbra tudjuk elhelyezni. Ebben a példában a Buy Stopot nél helyeztük el ami természetesen megengedett. 70. ábra. Függőben levő megbízásokat az aktuális árnál alacsonyabban és magasabban adhatunk. A függőben levő Buy Stop megbízás ön. A függőben levő Sell Limit megbízás n. A függőben levő Sell Stop megbízás ön. A függőben levő Buy Limit megbízás on. A fenti példában, minden függőben levő megbízást a nulla bárnál adtunk, a 70. ábrán látható időpillanatban, a minimális távolság a függőben levő megbízások elhelyezésére 5 pont. Sell Stop megbízást adunk a piaci árhoz a lehető legközelebbi áron. Ebben az esetben, Bid = és Sell Stop = , most a megbízás és kettős ár megfelelő értéke(bid) közötti távolság,6 pont ( ), ez több mint a szükséges minimum. Ez azt jelenti, hogy a megnyitásánál (mint minden másik megbízás esetében), a kereskedelmi kérést az ügyfélterminál jóváhagyta és elküldte a szervernek. A szerver szintén ellenőrizte a teljesítés követelményeit és úgy döntött, hogy végrehajtja a függőben levő megbízás elhelyezése iránti kérést (lásd: Követelmények és korlátozások a kereskedelemben). A stop megbízások pozícióját, amiket csatoltunk a függőben levő megbízásokhoz, szintén korlátozza a minimális távolság: Egy függőben levő megbízás csatolt stop szintjeinek pozíciójára vonatkozó korlátozásokat a függőben levő megbízás kért pozíciójából számoljuk, és nincs kapcsolata a megbízás elküldésekori piaci árral. A StopLoss és Take Profit egy függőben levő megbízásban sem lehet közelebb a kért nyitó árhoz, mint a minimális távolság. A StopLoss és Take Profit pozícióit a függőben levő megbízásokban nem korlátozza a fagyott zóna. A 71. ábrán függőben levő Sell Limit megbízást látunk, ahol a stop megbízásokat olyan közel helyezzük el a kívánt nyitó árhoz amilyen közel csak lehetséges. Ebben az esetben, a kért nyitási ár = , StopLoss=1.2949, Take Profit= pont minimális távolságnál ezek az értékek megengedettek

164 71. ábra. A függőben levő megbízás stop szintjei, a megbízáshoz a lehető legközelebb elhelyezve. Ebben a példában, Sell Limit függőben levő megbízást 18:07-kor adtuk. A 71. ábrán látható, hogy azután a piaci ár keresztezte az egyik stopmegbízást és azután megint lement. Ez az esemény sehogy nem befolyásolta a függőben levő megbízást: egy stop megbízás csak bezárhatja a nyitott tőzsdei megbízást, ez csak akkor válik hatásossá, amikor a függőben levő megbízás teljesül. Ebben az esetben a függőben lévő megbízás még nem teljesült (mert az aktuális ár nem érte el a beállított nyitó árat), tehát a stop megbízás elérése nem okozott semmilyen változást. A függőben levő megbízások törlésével kapcsolatos korlátozásokat a korrekt piaci ár alapján számoljuk ki, ezen megbízások módosítására is ez a szabály vonatkozik. A Buy Limit, Buy Stop, SellLimit és Sell Stop függőben levő megbízásokat nem tudjuk törölni, ha a megbízás beállított nyitó ára a piaci árhoz viszonyított befagyott zónán belül van. A Sell Limit függőben lévő megbízást a 71. ábra szerint csak akkor tudja törölni az ügyfélterminál, ha a fagyott zóna az ábrázolt pillanatban egyenlő vagy kisebb, mint 8. Ebben az esetben, a fagyott zóna felső széle = = A függőben lévő megbízás nyitó ára , ez az ár a fagyott zónán kívülre esik ezért törölhető. Ha a bróker 8 pontnál nagyobbra állítja be a fagyott zóna értékét, akkor a függőben levő Sell Limit megbízást nem tudja törölni az ügyfélterminál, visszautasítja a kereskedelmi kérést. A függőben levő megbízások módosítása piaci megbízásokra A függőben levő megbízások piaci megbízásra történő módosítása a szerveren automatikusan történik, úgyhogy nincs erre a műveletre szolgáló függvény (lásd: Követelmények és korlátozások a kereskedelemben). A függőben levő Buy Limit és Buy Stop megbízások piaci megbízássá válnak, ha az utoljára ismert Ask ár eléri a függőben levő megbízási árat. A függőben levő Sell Limit és Sell Stop megbízások piaci megbízássá válnak, ha az utoljára ismert Bid ár eléri a függőben levő megbízási árat. A 70. ábrán lévő függőben levő megbízásokkal kapcsolatban, a következőket mondhatjuk el: A függőben levő Buy Stop megbízás Buy piaci megbízássá válik, ha az aktuális Ask eléri az ös értéket. A függőben levő Sell Limit megbízás Sell piaci megbízássá válik, ha az aktuális Bid eléri az es értéket. A függőben levő Sell Stopot megbízás Sell piaci megbízássá válik, ha az aktuális Bid eléri az ös értéket

165 A függőben levő Buy Limit megbízás Buy piaci megbízássá válik, ha az aktuális Ask eléri az as értéket. Az ezután következő, az ezekkel a megbízásokkal kapcsolatos események a ábrákon követhetők. 72. ábra: A függő megbízásból piaci megbízás lesz. A továbbiakban a 2 másik függőben levő megbízásból szintén piaci megbízás válik. 73. ábra: A függő megbízásokból piaci megbízás lesz. 74. ábra. A módosult (piaci) megbízások megjelennek a Terminál ablakban. Jegyezd meg, hogy a 73. ábra a Buy megbízás nyitását mutatja (a korábbi függőben levő Buy Stop megbízás). Látható, hogy az a bár, amit 18:55-kor nyílt, nem érinti meg az ös árat. A legmagasabb ár ezen a báron belül Ugyanakkor, a Terminal ablakban (74. ábra) azt látjuk, hogy a függőben levő megbízásból ez alatt a bár alatt lett piaci megbízás, vagyis 18:55-kor. Itt kell még egyszer hangsúlyozni, hogy a szimbólum ablakban csak a történelmi kettős ár alacsonyabb értékét tüntetik fel, vagyis a Bid ár történelmi változását. Az Ask ár alakulását nem látjuk. Ezért gondolhatod azt, hogy a függőben levő megbízásból valamilyen hiba miatt lett piaci megbízás. Pedig ebben az esetben nincs hiba. Akkor, amikor a Bid ár egyenlő volt mal, az Ask ár = volt. (2 pont a spread). Így a piaci ár megérintette a beállított megbízási árat és az a függőben levő megbízás automatikusan piaci megbízássá vált. A megbízás végrehajtása a szerveroldalon történt

166 Közvetlenül azután a szerver megküldte az ezzel kapcsolatos információt az ügyfélterminálnak, és az két helyen is megjelenítette az információt: a szimbólumablakban (grafikusan) és a terminál ablakban (szövegesen). A hasonló megfigyeléseket tehetünk a Buy Limit megbízás végrehajtására vonatkozóan. Bár a grafikusan megjelenített ár érintkezik a függőben levő Buy Limit megbízással 16:37-kor, 16:39-kor és 16:41-kor (72. ábra), a megbízás nem teljesül. Ebben az esetben ennek az oka ugyanaz: a piaci Ask ár nem érintette meg a kért megbízási árat. Azonban a következő báron belül 16:42-kor megérintette azt a szintet. Ennek következtében a függőben levő megbízás piaci megbízásra módosult, tehát Buy Limitet a szimbólumablakban lecserélték Buyra, és az új megbízás megjelenik a Terminál ablakban. A fenti példában minden megbízást nulla stop megbízás csatolásával adtunk (vagyis stop megbízások nélkül). Azonban a stop megbízások megléte, és a stopmegbízások értékei nem fogják befolyásolni a függőben levő megbízások módosítását piaci megbízásra, őket csak az az esemény tudja piaci megbízásra módosítani, ha a kettős ár megfelelő ára keresztezi vagy megérinti a függőben levő megbízás nyitó árát. Egy függőben levő megbízás pici megbízássá módosulását nem befolyásolják a hozzá csatolt stop megbízások. Egy függőben levő megbízást teljesíthetnek (módosíthatják piaci megbízásra) egy olyan árnál is ami nem azonos a függőben levő megbízás kért nyitó árával. Ez megtörténhet a piaci ár gyors változásánál, amikor a legutóbbi ismert ár a megbízás megnyitása előtt még nem érte el a kért árat, de a következő ár (aminél a megbízás teljesül) már túllépett ezen (75. ábra). a) két bár közti rés, b) a báron belül létrejövő rés 75. ábra. Függőben levő megbízás teljesülése a résben. A 75. ábrán láthatjuk a Buy Stop függőben levő megbízás egy lehetséges variációját (az ábrán a megnyitás két fázisát láthatjuk, a megnyitás előttit és megnyitás utánit; a valóságban vagy a Buy Stop, vagy a Buy megbízást láthatjuk, de mindkettőt egyszerre nem). A legújabb ismert ár a rés előtt volt. 19:15-kor valamilyen hír érkezett, melynek hatására a szimbólum árában egy ugrás történt. A hírek kibocsájtása utáni először ismert ár Általában az árak fel, vagy le ugranak fontos hírek után. Ilyen esetben, a bróker nem tudja a megbízást teljesíteni a kért árnál, mert megfelelő árak abban a pillanatban a piacon nincsenek. Ebben az esetben, a függőben levő Buy Limit megbízást en helyeztük el, de ez ös árnál teljesült. Ez abból ered, hogy nem volt az től ig terjedő a tartományon belül semmilyen másik ár. A szóban forgó események következtében a Buy megbízást egy olyan árnál nyitották, ami 25 ponttal rosszabb lett, mint a függőben Buy Stop megbízás. Hasonló a helyzet (a vártnál kevesebb profit) ha a Sell Stop megbízás esetén az ár leesik. Azonban, ha függőben levő Buy Limit vagy Sell Limit megbízásunk van a rés idején, a megbízás teljesülhet egy olyan árnál is, ami kedvezőbb a kereskedőnek a saját megbízási áránál. Tudni kell, hogy a rés (a két legközelebbi ár közötti különbség több mint egy pont) meglehetősen gyakori és bármikor előfordulhat. Ha az árrés két bár között képződik, vagyis közvetlenül az új bár első tickje előtt, akkor a rés felismerhető a charton (75. ábra). Azonban, ha az árrés egy báron belül keletkezik, nem tudjuk észlelni ezt a rést vizuálisan (75. b ábra). Ebben az esetben a rés elbújik a báron belül (a gyertyában). A bárokon belüli réseket sem ránézésre, sem semmilyen rendelkezésre álló programjellemző alapján

167 visszamenőleg nem lehet megtalálni, (azonban észlelni lehet a rést, ha használsz egy olyan alkalmazási programot, ami figyeli a beérkező árak közti különbséget). A megbízások módosítása A MetaTrader 4 kereskedő platform lehetővé teszi olyan kereskedelmi kérések küldését, amelyek módosítják a piaci és függőben levő megbízások árszintjeit. (Minden megbízás módosítására az OrderModify() függvényt kell használni). Ha egy megbízás már teljesült (megnyílt), akkor csak a stop szinteket lehet módosítani. Nem változtathatjuk meg a nyitó árakat a teljesült megbízásokban. Nem tudod megváltoztatni egy tőzsdei megbízás nyitó árát, ha a megbízás nyitása már megtörtént. Ezt semmilyen programozói módszerrel nem lehet megtenni. Az egyetlen dolog, amit a megbízással tenni tudsz az, hogy bezárod. Egy tőzsdei megbízást zárni lehet egy kereskedelmi kérés végrehajtásával, amit a program vagy a kereskedő indít, vagy autometikusan bezárul, ha a piaci ár eléri a megadott stop árat. A Stop Loss és Take Profit megbízást nem tudjuk a piaci árhoz közelebb tenni, mint a minimális távolság. Egy megbízást nem módosíthatunk, ha a StopLoss vagy a Take Profit piaci ártól számított fagyott zónán belül van. Jegyezd meg, egy tőzsdei megbízás stop pozícióira vonatkozó korlátozást az aktuális piaci árhoz viszonyítjuk és ennek nincs kapcsolata a megbízás nyitó árával (lásd: Követelmények és korlátozások a kereskedelemben). Ezért a stop szinteket elhelyezhetjük, az aktuális ártól függően a megbízási ár alá vagy fölé. Nézzünk meg egy példát! Mielőtt egy megbízást nyitottunk a stop megbízások a lehető legközelebb vannak a piaci árhoz (69.ábra). Azután a piaci ár változott (1 ponttal növekedett). A 76. ábrán látható pillanatban lehetségessé vált, hogy megváltoztassuk a Take Profit értékét. A Sell megbízások zárása a legújabb ismert Ask árnál történik. A távolság az Ask= és az előző Take Profit= érték között 6 pont, ez nagyobb, mint a megengedett minimum távolság. A kereskedő (vagy egy program) küldött egy kereskedelmi kérést, hogy növeljék meg a Take Profit értékét 1 ponttal. Ez azzal végződik, hogy csináltunk egy kereskedelmi műveletet, amivel megváltoztattuk a tőzsdei megbízás stop pozícióját (Take Profit= értékről az új értékre). Ha a Sell megbízás módosítására olyan kereskedelmi kérés adunk, ahol valamelyik stop szint közelebb van az Ask piaci árhoz, mint a minimális távolság, ezt a kereskedelmi kérést visszautasítja az ügyfélterminál és a megbízást nem hajtja végre. 76. ábra: A megbízás módosítása, a stop megbízás a lehető legközelebb van a piaci árhoz. A megbízások módosítási szabálya a stop megbízások aktuális árhoz viszonyított távolságát korlátozza, de nem korlátozza a nyitási ártól való távolságot. Ezért stop megbízásokat bármilyen árra adhatunk, amelynek az aktuálisártól való távolsága nagyobb, mint a korlátozó távolság (ha a megbízás módosításának

168 pillanatában, a stop megbízás a fagyott zónán kívül van). 77. ábrán látunk még egy megbízás módosítást: ebben az esetben a stop megbízások jóval a korlátozó minimális távolságon kívül vannak. 77. ábra: A megbízás módosítása, a stop megbízást a minimális távolságon túl adjuk. A függőben levő megbízások módosítása A minden megbízást, így a függőben levő megbízást is az OrderModify() függvénnyel módosítunk. Egy függőben levő megbízás módosítása azt jelenti, hogy megváltoztatjuk a függőben levő megbízás nyitó árát és az előre meghatározott stop szinteket. A függőben levő megbízások pozíciójának módosításával kapcsolatos korlátozásokat az aktuális piaci ár alapján számoljuk ki, a függőben levő megbízás nyitó árára alkalmazva. A függőben levő megbízás stop pozícióira vonatkozó korlátozást a függőben levő megbízás kért nyitó ára alapján számoljuk, és ezeknek nincs kapcsoltuk a piaci árakkal. A függőben levő Buy Limit és Buy Stop megbízást nem tudjuk a piaci Ask árhoz közelebb tenni, mint a minimális Stop Level. A függőben levő Sell Limit és Sell Stop megbízást nem tudjuk a piaci Bid árhoz közelebb tenni, mint a minimális Stop Level. Egy függőben levő megbízás StopLoss/Take Profitját nem tehetjük a kért megbízás nyitó árához közelebb, mint a minimális Stop Level. A függőben levő Buy Limit és Buy Stop megbízást nem tudjuk módosítani, ha a módosítandó nyitó ár a piaci Ask ártól számított fagyott zónán belül van. A függőben levő Sell Limit és Sell Stop megbízást nem tudjuk módosítani, ha a módosítandó nyitó ár a piaci Bid ártól számított fagyott zónán belül van. A függőben levő StopLoss és Take Profit megbízások pozícióit nem korlátozza a fagyott zóna (Freeze Level). Jegyezd meg: egy függőben levő megbízás kért ára vonatkozó korlátozást a piaci ártól számoljuk, míg a függőben levő megbízás stop szintjeire vonatkozó korlátozást a kért nyitó ártól (lásd: Követelmények és korlátozások a kereskedelemben). Például, a függőben levő Buy Limit megbízást a következő paraméterekkel helyeztük el: kért ár= , StopLoss=1.2964, Take Profit= A piaci ár aktuális értéke Ask= A megbízást 14 pont ( ) távolságra helyeztük el a piaci ártól, ami messze felülmúlja a megengedett minimális távolságot. A stop megbízások 5 pont távolságra vannak a kért árról, ami nem kisebb, mint a minimális távolság, úgyhogy ez megengedett

169 78. ábra. Függőben levő Buy Limit megbízás csatolt stop szintekkel a nyitási megbízáshoz a lehető legközelebb elhelyezve. Ha a kereskedő meg akarja változtatnia a Buy Limit megbízás nyitási árát valamilyen irányban, ebben az esetben egyidejűleg meg kell változtatni a megfelelő stop pozíciót is (vagy törölni azt, nulla értéket beállítani). Különben a távolság a megbízás és a csatolt stop szint között kevesebb lehet a megengedett minimum távolságnál. A kereskedő úgy döntött, hogy módosítja a megbízást, úgy, hogy az 5 pont távolságot megtartja a megbízás és a Take Profit között, míg a StopLoss értéke változatlan maradt (79. ábra). 79. ábra. Módosított Buy Limit megbízás (a kért árat és a Take Profit szintet megváltoztatják). Ha a kereskedőnek a piaci árhoz a lehető legközelibb Buy Limit függőben levő megbízást kell adnia, ebben az esetben (80. ábra), a kért ár megengedett értéke Ask-5pont = = Ebben a példában a stop megbízások a korlátozó minimális távolságon kívül esnek. 80. ábra.buy Limit megbízás módosítása a lehető legközelebb a piaci árhoz. A Követelmények és korlátozások a kereskedelemben függelékben található egy olyan összefoglaló táblázat, ami részletezi a kettős árak alkalmazását a megbízások nyitásakor, zárásakor és módosításakor, valamint a korlátozások meghatározásakor

170 Megbízások nyitása és elhelyezése Megbízások nyitására és függőben levő megbízások elhelyezésére az OrderSend() függvényt használjuk. Az OrderSend() függvény int OrderSend (string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=null, int magic=0, datetime expiration=0, color arrow_color=clr_none) (jegyezd meg: itt és lentebb a függvényfejlécet elemezzük, és ez nem egy példa arra, hogy hogyan használjuk a függvényhívást egy programban). Vizsgáljuk meg részletesen, hogy ez a függvény miből áll! OrderSend a függvény név. A függvény visszaküldi a jegyzési számot (jegyzés, 'ticket' a megbízás egyedi azonosító száma) amit a kereskedelmi szerver rendel a megbízáshoz, vagy -1-et küld vissza, ha a kereskedelmi kérést visszautasította a szerver, vagy az ügyfélterminál. Azért hogy a kereskedelmi kérés elutasításának az okáról információt kapj, GetLastError() függvényt kell használnod (lent meg fogunk tárgyalni a leggyakoribb hibák közül néhányat). symbol a kereskedés tárgyának neve. Mindegyik szimbólum egy string változó. Például az Euro/amerikai dollár devizapárnál ez az érték EURUSD. Ha a megbízást egy előre meghatározott szimbólumon nyitjuk, ezt a paramétert egyértelműen megadhatjuk: EURUSD, EURGBP, stb. Azonban, ha Expert Advisort használsz valamilyen szimbólum ablakban, használhatod a beépített Symbol() függvényt. Ez a függvény visszaküld egy olyan string értéket, ami annak a szimbólum nevének felel meg amelyik szimbólum ablakában az EA-t vagy a scriptet végrehajtjuk. cmd a művelet típusa. A művelet típusa egy előre definiált állandó vagy annak az értéke, és a kereskedés típusát határozza meg. volume a kereskedés tárgyának mennyisége. A megbízások előtt mindig ellenőrizni kell a számla egyenleget. Függőben levő megbízások fedezetének összege nem korlátozott. price a nyitó ár. A rá vonatkozó követelmények és korlátozások alapján határozzuk meg (lásd: A megbízások jellemzői és a kereskedés végrehajtás szabályai ). Ha az ár, amit a nyitási kérelemben megadtunk nem létezik vagy eltér az aktuális piaci ártól, a kereskedelmi kérést visszautasítják. Azonban, ha az ár eltér az aktuális ártól, de az eltérés a slippage (csúszás) értékén belül van, ezt a kereskedelmi kérést az ügyfélterminál el fogja fogadni és elküldi a kereskedelmi szervernek. slippage, csúszás a kért nyitó ár maximális megengedett eltérése a valós piaci ártól (pontokban). Ezt a paramétert nem használjuk a függőben levő megbízások elhelyezésekor. stoploss az a kért záró ár, ami meghatározza a maximális veszteséget az adott üzleten. A beállításkor figyelembe kell venni a rá vonatkozó követelményeket és korlátozásokat (lásd: A megbízások jellemzői és a kereskedés végrehajtás szabályai, Követelmények és korlátozások a kereskedelemben). takeprofit az a kért záró ár, ami meghatározza a maximális nyereséget az adott üzleten. A beállításkor figyelembe kell venni a rá vonatkozó követelményeket és korlátozásokat (lásd A megbízások jellemzői és a kereskedés végrehajtás szabályai, Követelmények és korlátozások a kereskedelemben). comment a megjegyzés szövege. A megjegyzés utolsó részét módosíthatja a kereskedelmi szerver. magic a magic number. A felhasználó megbízásának azonosítására használhatjuk. Néhány esetben ez az egyetlen információ, ami segít azonosítani, hogy a megbízás melyik programhoz tartozik, melyik nyitotta azt. A paramétert a felhasználó állítja be; az értéke azonos vagy különböző lehet több megbízás esetében. expiration a lejárat a dátuma. Amint ez a idő elérkezik, a függőben levő megbízás automatikusan törölve lesz a szerveroldalon. Néhány kereskedelmi szerveren nem lehet beállítani a függőben levő megbízás lejárati idejét. Ebben az esetben, ha megpróbálod beállítani ezt a paraméter egy nem nulla értékre, a kérést vissza fogják utasítani

171 arrow_color a megbízás nyitását jelző nyíl színe az ábrában. Ha ez a paraméter hiányzik, vagy az értéke CLR_NONE, a nyitás helye nem jelenik meg az ábrán. Néhány kereskedelmi szerveren korlátozott a nyitott és függőben levő megbízások száma. Ha ezt a határt elértük, minden kereskedelmi kérést, ami megbízás nyitására, vagy függőben levő megbízás elhelyezésére irányul, vissza fog utasítani a kereskedelmi szerver. Megbízások nyitása Először úgy tűnhet, hogy az OrderSend() függvény túl bonyolult. Azonban a szóban forgó paraméterek egyszerűek, és sikeresen használhatjuk őket a kereskedelmedben. Azért hogy ezt belássuk, vizsgáljuk meg a legegyszerűbb variációt, amivel az OrderSend() kereskedelmi függvényt egy megbízás nyitására használunk. Először is meg kell jegyeznünk, hogy az OrderSend() függvénynek előre definiált paraméterei vannak (lásd: Függvény hívás, Függvény leírás és 'return' operátor). Ez azt jelenti, hogy ezt a függvényt leegyszerűsített módban használhatjuk, ami a minimális paraméter készletet igényel. Ezek a paraméterek a következők: symbol egy szükséges paraméter, mert tudnunk kell, hogy mivel akarunk kereskedni. Ha azt akarjuk, hogy a programunk minden szimbólum ablakában működjön, akkor ezt a paramétert a Symbol() függvénnyel kell helyettesíteni; cmd például: nyissunk egy piaci Buy megbízást; ebben az esetben az OP_BUY paramétert fogjuk megadni; volume - itt tudjuk megadni a kereskedés nagyságát, például, 0.1 lot; price - a Buy megbízás Ask ára; slippage - a csúszást általában 0-3 pontban határozzuk meg. Most legyen 2 pont; stoploss a stop megbízást nem tudjuk a megengedett minimum távolságnál közelebb elhelyezni, a megengedett minimum távolság rendszerint 5 pont (lásd: Követelmények és korlátozások a kereskedelemben); most tegyük a stop megbízást 15 pont távolságra a nyitó ártól, mégpedig: Bid - 15*Point; takeprofit - adjunk stop megbízást 15 pont távolságra a nyitó ártól: Bid + 15*Point; itt van a simpleopen.mq4 script, amivel elküldjük a Buy megbízást: // // simpleopen.mq4 // The code should be used for educational purpose only. // int start() // Special function start() // Opening BUY OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); return; // Exit start() // Ha elindítod ezt a scriptet, ez az esetek többségében működni fog. A script egy különleges függvényből áll, ami tartalmazza az OrderSend() megbízás megnyitó függvényt és a 'return' operátort. Írjuk le a végrehajtás algoritmust programsoronként és eseményenként! 1. A felhasználó csatolta a scriptet a szimbólumablakhoz azáltal, hogy az egérgombbal áthúzta a scriptnevet a navigátorból az ügyfélterminál azon szimbólum ablakába, amely szimbólumon nyitni akar egy 0.1 lot méretű Buy megbízást, a nyitási ártól 15 pont távolságra elhelyezett stop szintekkel. 2. A script a csatolásának a pillanatában, az ügyfélterminál átadja a vezérlést (azzal, hogy elindítja azt) a különleges start() függvénynek (itt kell emlékeztetnünk, hogy egy script start() függvénye abban a pillanatban elindul, amint a scriptet csatoljuk a szimbólumablakhoz, míg egy EA start() függvénye csak akkor indul el mikor a legközelebbi tick érkezik a szimbólumra)

172 3. A különleges start() függvény szerkezetén belül a a vezérlés lekerül abba a sorba, ami a megbízást megnyitó függvényt hívja: OrderSend(Symbol(),OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); Ennek a függvénynek a végrehajtása előtt a program kiszámítja minden formális paraméter értékét: 3.1. Mivel a scriptet az Eur/USd ablakához csatoltuk, a beépített Symbol() függvény az EURUSD string értéket fogja visszaküldeni A függvény hívás pillanatában Ask = és Bid = A StopLoss értéke ebben az esetben: * = , míg a TakeProfit = Az OrderSend() függvény végrehajtása : 4.1. A függvény kialakított egy megbízás megnyitása iránti kereskedelmi kérést, és átadta ezt a kérést az ügyfélterminálnak A függvény a kereskedelmi kérés küldésével egyidejűleg átadta a vezérlést az ügyfélterminálnak, ezért a küldő program végrehajtása leállt Az ügyfélterminál ellenőrizte a megkapott kereskedelmi kérést. Nem észlelt semmilyen helytelen paramétert, úgyhogy ez a kérést továbbküldte a szervernek A szerver megkapta a kereskedelmi kérést, ellenőrizte azt, nem észlelt semmilyen helytelen paramétert, és úgy döntött, hogy végrehajtja a kérést A szerver végrehajtotta a kérést azáltal, hogy csinált egy tranzakciót az adatbázisában, és a végrehajtott kéréssel kapcsolatos információt visszaküldte az ügyfélterminálnak Az ügyfélterminál megkapta az információt arról, hogy az utolsó kereskedelmi kérését végrehajtották, feltünteti ezt az eseményt a terminál ablakban és a szimbólumablakban, és visszaküldi a vezérlést a programba Ha visszakapta a vezérlést, a program továbbra is működni fog attól a programsortól, ahonnan a vezérlés korábban átkerült az ügyfélterminálhoz (és ahová később visszaküldték). Vedd észre, hogy a lépések alatt a program nem hajtott végre semmilyen műveletet - a program a szerver válaszára várakozott. 5. A program fölötti ellenőrzés a következő operátorhoz - a 'return' operátorhoz kerül. 6. A 'return' operátor végrehajtása azzal végződik, hogy kilép a start() függvényből, és a program végrehajtása befejeződik (emlékeztetnünk kell, hogy a scriptek befejezik a munkájukat a végrehajtásuk után) és a vezérlés visszakerül az ügyfélterminálhoz. A script teljesítette feladatát: a Buy megbízást az előre beállított paraméterekkel megnyitotta. A scriptek használata nagyon kényelmes, ha egy kicsi, egyszerű műveletet kell végrehajtani; ebben az esetben egy script használata ésszerű. A 4.6. lépéstől a kereskedő látja a megbízást a képernyőn

173 81.ábra. Megbízás adása a simpleopen.mq4 script segítségével. Az események nem mindig a fent bemutatott módon zajlanak. Lehetséges, hogy a kereskedelmi kérést visszautasítja az ügyfélterminál vagy a szerver. Kísérletképpen próbáljuk meg, hogy például megváltoztatjuk a szimbólumnevet: legyen GBPUSD (ez megengedett). Ezzel a változtatással a következő programot fogjuk kapni: int start() // Special function start // Opening BUY OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); return; // Exit start() Indítsuk el a scriptet ugyanabban az Eur/Usd szimbólum ablakban. A scripttől azt várjuk, hogy nyisson egy megbízást a Gbp/Usd ablakban. Azonban, mivel a scriptet az Eur/Usd ablakhoz csatoltuk, nem nyithat megbízást a Gbp/Usd ablakban. Az ilyen programok hátránya a funkcionális korlátozásuk. Ebben az esetben, a felhasználó hozzácsatolta a scriptet a szimbólumablakhoz és vár arra, hogy a megbízás teljesüljön. Azonban a megbízás nem teljesül. A felhasználó nem tudja, hogy ennek mi az oka: a programkódban rejlő algoritmikus hiba, a kereskedelmi kérés elveszett a szerverre vezető úton, vagy a kereskedelmi kérést visszautasította az ügyfélterminál, vagy egyéb más akadály történt. Hogy a felhasználót ellássuk (és nagyon fontos, hogy a programot is) a kereskedelmi kérés végrehajtásával kapcsolatos információval, szükség van a hibák feldolgozására. Hibafeldolgozás Az ügyfélterminál egy nagyon fontos tulajdonsága az, hogy ha egy hiba történik egy alkalmazás végrehajtása alatt, az ügyfélterminál nem állítja le a program végrehajtását. Hibákat okoz az alkalmazásban használt algoritmus tökéletlensége. Néhány esetben hibát okoz valamely külső tényező (programra gyakorolt hatása). A hibák belső okai lehetnek az MQL4 követelményeinek bármilyen megszegése, vagy kereskedés szabályainak megsértése, például érvénytelen árak használata. A külső okok azok, amelyek nem kapcsolatosak az alkalmazási programmal, mint például a félbeszakadt kapcsolat. Ha egy hiba történik egy program végrehajtása során, a program továbbra is futni fog, és az ügyfélterminál létre fogja hozni a GetLastError() függvényen keresztül a hibakód-értékét. A GetLastError() függvény int GetLastError() A függvény visszaküldi az újonnan történt hiba kódját, azután a különleges last_error változó értéke tárolja az utolsó hiba kódját. A következő GetLastError() hívás 0-t fog visszaküldeni. Az alábbiakban a hibakód alapján azonosítani fogunk minden lehetséges hibát. Több hiba történhet egy program végrehajtása alatt; a GetLastError() függvény csak a legújabb hiba kódértékét küldi vissza, ezért minden alkalommal, amikor szükségünk van erre az információra, ajánlott a GetLastError() függvényt futtatni, közvetlenül azután a programsor után, amiben a hiba történhetett

174 Hiba 130. érvénytelen stop megbízás Az utoljára vizsgált script nem elemzi a hibákat, ezért a felhasználó továbbra is tájékozatlan marad a megbízás végrehajtásának a folyamatát illetően. A GetLastError() függvény használatának egyszerűbb variációjában a program elemez egy hibát és erről tájékoztatja a felhasználót. Ha futtatod a confined.mq4 scriptet az Eur/Usd ablakban, egy hiba fog történni. // // confined.mq4 // The code should be used for educational purpose only. // int start() // Special function start // Opening BUY OrderSend("GBPUSD",OP_BUY,0.1,Ask,3,Bid-15*Point,Bid+15*Point); Alert (GetLastError()); // Error message return; // Exit start() // Csak egy sort adtunk hozzá, de ettől nagyon informatív lett a script: Alert (GetLastError()); // Error message A GetLastError() függvény visszaküldi az utolsó hiba kódját, amit az Alert() függvény arra használ, hogy bemutassa ezt az értéket a képernyőn. Miután confined.mq4 scriptet csatoltuk az Eur/Usd szimbólumablakhoz, a script végrehajtásra kerül, melynek következtében a felhasználó a következő üzenetet fogja látni: 82. ábra. A confined.mq4 eur/usd ablak történt végrehajtásakor kapott hibakód. A Függelék hibakód jegyzékében megkereshetjük, hogy milyen hiba történt a program végrehajtásánál. Ebben az esetben, 130-as hiba (az érvénytelen stop megbízás) történt. Ez azt jelenti, hogy a formális paraméterek értékei, amiket az OrderSend() függvényben használtunk, nem tartják be azokat a feltételeket, amiket a Követelmények és korlátozások a kereskedelemben című fejezetben ismertetünk. Nézzük meg alaposabban mi okozta a hibát: az aktuális Bid és Ask piaci árakat az Eur/Usd szimbólumablakból vettük, amihez a scriptet csatoltuk. Azonban ezeket az értékeket arra használjuk, hogy egy Gbp/Usd iránti kereskedelmi kérést alakítsunk ki. Ezért a Gbp/Usd Ask = aktuális áránál, az új megbízás aktuális TakeProfit értéke (Eur/Usd Bid =1.2930) *0.0001= , ami lényegesen alacsonyabb a minimális megengedett értéknél, vagyis érvénytelen. Ebben az esetben egy algoritmikus hiba történt. Azért hogy ezt ki javítsd, a szimbólumárak helyes értékeit kell használnod. Ezeket az értékeket a MarketInfo() függvény alkalmazásával kaphatjuk meg. Az improved.mq4 megnyitja a Gbp/Usd megbízást, bármilyen szimbólumablakban indítjuk el: // // improved.mq4 // The code should be used for educational purpose only. // int start() // Special function start double bid =MarketInfo("GBPUSD",MODE_BID); // Request for the value of Bid double ask =MarketInfo("GBPUSD",MODE_ASK); // Request for the value of Ask double point =MarketInfo("GBPUSD",MODE_POINT);//Request for Point

175 // Opening BUY OrderSend("GBPUSD",OP_BUY,0.1,ask,3,bid-15*Point,bid+15*Point); Alert (GetLastError()); // Error message return; // Exit start() // A fenti hiba nem történik meg ennek a scriptnek a végrehajtásakor, a végrehajtás azzal fog végződni, hogy a küvetkező üzenetet kapjuk: 0 (nulla). Ez azt jelenti, hogy a GetLastError() függvény a 0 értéket küldte vissza, az ügyfélterminál nem talált hibákat a kereskedelmi kérés végrehajtásában. Vizsgáljunk meg egyéb gyakori hibákat! Térjünk vissza arra a módszerre, hogy a megbízást ugyanazon a szimbólumon nyitjuk, mint amelyik szimbólum ablakához a scriptet hozzáerősítettük. Hiba 129. érvénytelen ár Néhány esetben egyszerű hiba történik - a kettős ár hibás értékét adjuk meg nyitó árként. A Buy megbízásokat (lásd: Követelmények és korlátozások a kereskedelemben) az Ask árnál nyitjuk. Lent bemutatjuk, mi történik, ha tévedésből a scriptben a Bid árat adjuk meg mistaken.mq4: // // mistaken.mq4 // The code should be used for educational purpose only. // int start() // Special function start // Opening BUY OrderSend(Symbol(),OP_BUY,0.1,Bid,3,Bid-15*Point,Bid+15*Point); Alert (GetLastError()); // Error message return; // Exit start() // Mielőtt a kereskedelmi kérést elküldi a szervernek, az ügyfélterminál elemzi, hogy az ár és stop kért értékei betartják-e a megengedett értékekre vonatkozó szabályokat. Ezalatt az ellenőrzés alatt kiderül, hogy a kért nyitó ár hibás, úgyhogy az ügyfélterminál nem fogja a kereskedelmi kérést elküldeni a szervernek. A GetLastError() függvény a 129-es értéket fogja visszaküldeni (lásd: Hibakódok). A script végrehajtása a megfelelő hibaüzenet megjelenésével fog végződni: 83. ábra. Hiba 129 (érvénytelen ár) a mistaken.mq4 végrehajtásával. Hiba 134. nincs elég pénz a kereskedéshez Hasonló eredményt (hiba 134) kapunk, ha nincs elegendő szabad pénz, ekkor semmi szín alatt nem teljesül a megbízás. Megtudhatod, hogy mennyi fedezet kell 1 lot méretű megbízás megnyitásához a MarketInfo(symbol_name, MODE_MARGINREQUIRED) függvény használatával

176 Egy standard méretű lot mérete ugyanazon a szimbólumon változhat a különböző dealing centerekben. A megbízáshoz szükséges szabad egyenleg fordítottan arányos a tőkeáttétellel. Ugyanakkor valamely a szimbólumon 1 pont értéke független a tőkeáttételtől. 3. táblázat. 1 lot kötésméret 1 pont értékének lehetséges variációi (a letéti pénznem US dollár). Dealing Center 1 Dealing Center 2 Dealing Center 3 Buy Sell 1pt Buy Sell 1pt Buy Sell 1pt EUR/USD GBP/USD AUD/USD USD/JPY USD/CHF EUR/CHF árak. Vizsgáljuk meg, hogy milyen módszerrel számolják ki 1 lot méretű kötésnél 1 pont értékét. 1. Dealing Center (a leggyakoribb) Azokon a szimbólumokon, ahol az USD szerepel a nevezőben, 1 lot fedezete egyenlő az aktuális kettős ár megfelelő értékének 1000 szeresével, míg 1 pont értéke egyenlő 10 dollárral. Azokon a szimbólumokon, ahol az USD a számlálóban szerepel, 1 lot fedezete egyenlő dollárral, míg 1 pont értéke fordítottan arányos az aktuális szimbólum másik tagjával, 1/(Bid). Például USD/CHF-en Bid= és 1 pont értéke 10/ = A kereszt devizák esetében, 1 lot fedezetét a számláló pénznemére számítjuk, míg 1 pont értéket a nevező pénznemére. Például: az EUR/CHF-en 1 lot letéti igénye (az EUR/USD alapján), míg 1 pont értéke 8.02 (az USD/CHF alapján). 2. Dealing Center Néhány dealing center esetében a fedezet és a pontérték számításának a szabálya szimbólumonként különböző lehet. Például: 1 lot fedezete és 1 pont értéke arányosan nagyobb vagy kisebb lehet, mint az 1. Például ez a tényező GBP/USD esetén 0.75 lehet, míg AUD/USD esetén 2.0. Az értékek ilyen meghatározása nem jár semmilyen gazdasági változással; ilyen esetben meg kell fontolni ezt a sajátosságot a megbízás adásakor. Szintén figyelembe kell venni, hogy 1 lot buy és sell mérete kereszt devizák esetében esetén ugyanaz. 3. Dealing Center Vannak olyan dealing centerek is, ahol 1lot fedezet igénye dollár minden szimbólumon. Ugyanakkor 1 pont értéke továbbra is arányos az aktuális. Ennek következtében a tőkeáttétel mindegyik szimbólumon más. 1 pont értékét, minded olyan szimbólumon, amit nem az USD-hez viszonyítva jegyzünk, a nevezőben szereplő devizához viszonyítunk. Általában nincs több módszer a fenti értékek meghatározására. De azt hiszem szükségtelen hangsúlyozni, hogy a valódi pénzel történő kereskedést megelőzően meg kell győződni róla, hogy a dealing center milyen számítási módszert használ, és ezt a módszert figyelembe kell venni a kódolás során. Szabad margin Kódolásnál nagyon fontos figyelembe venni a felhasználható egyenleget. Szabad margin az az összeg, ami a kereskedelem végrehajtásakor rendelkezésre áll

177 Lássunk egy példát! Legyen az egyenleg ( Balance) , nyitott megbízások nincsenek a terminálban. Nyissuk egy 1 lot nagyságú Buy megbízást a 3. dealing center feltételei szerint. A következő szabályt is alkalmazza a 3. dealing center: Ha különböző irányú megbízásokat nyitunk egy szimbólumon, az azonos irányban kötött megbízások margin összege közül a kisebb a szabad margint növeli (ez a szabály nem alkalmazható minden dealing centerben). A terminál ablak mutatni fogja a nyitott megbízásokkal kapcsolatos információt. Jegyezd meg, a margin és megbízás nyitásakor a profit , azért a szabad margin = : 84. ábra. Buy megbízás a terminál ablakban. HIBÁS!! Miután ugyanakkora Sell megbízást nyitottunk, a szabad margin növekedni fog. A két ellentétes megbízás közül a kisebb fedezet igénye a szabad margint növeli, az dolláros sell megbízás miatt a szabad margin dollárral növekedni fog. A 85. ábrán azt az esetet látjuk, amikor az ellentétes megbízások margin igénye egyenlő és ezek kiegyenlítik egymást. 85. ábra. Buy és Sell megbízás a terminál ablakban. Amikor egy kisebb margin igényű Sell megbízást nyitunk a szabad margin szintén növekedni fog. Ebben az esetben a kisebb margin igényű kötési irány margin igénye dollár, ami a szabad margint dollárral fogja növelni, és a margin igény a különböző irányú megbízások összesített fedezet igényei közti különbség lesz (86. ábra). 86. ábra. Buy és Sell megbízás a terminál ablakban. Ha még egy 0.1 lot nagyságú Sell megbízást nyitunk ( fedezettel), az irányonkénti összesített fedezet igények közül a kisebb = lesz. Ezért a margin (azzal a helyzettel összehasonlítva, ahol csak a Buy megbízás volt nyitva) dollárral csökkent. A 86. ábrával összehasonlítva, a margin csökken, míg a szabad tőke dollárral növekszik (lásd a 87. ábrát)

178 87. ábra. Buy és Sell megbízások a terminál ablakban. A Free Margin a 86. és 87. ábrán több mint dollárral különbözik, mert az összes profit az aktuális ár változása miatt csökkent (a csökkenés 8.00). Ha hasonló manipulációkat végzünk egy másik dealing centerben akkor megfigyelhetjük, hogy a szabad margin meghatározása más módszerrel történik. Néhány dealing center a következő szabályt alkalmazza: Semmilyen megbízás nyitása nem szabadít fel tőkét és/vagy nem növeli meg a szabad margint. Azonban az ellentétes megbízások megnyitása nem köt le újabb tőkét, a megbízások tőkeigénye a különböző irányú megbízások integrált tőkeigényei közül a magasabb érték lesz, (a szabályt nem alkalmazza minden dealing center). Például, ha korábban nyitottál egy 4 lotos USD/JPY Buy megbízást 2. dealing centerben, a tőke és a szabad margin nem fognak megváltozni egy 4 lotos Sell megbízás megnyitásnál. 88. ábra. A különböző irányú megbízások nem módosítják a tőkét. Számításokat végezhetsz, hogy megtudd, az aktuális tőke elég-e egy megbízás megnyitására. Ezen kívül használhatod az AccountFreeMarginCheck() függvényt, amely visszaküldi a maradék szabad margin értékét, ami a meghatározott nagyságú, adott szimbólumon történő megbízás megkötése után maradna. Ha a visszaküldött érték egyenlő vagy több mint 0, akkor van elég pénz a számlán. Ha ez kevesebb 0-nál, akkor ilyen méretű megbízás ezen a szimbólumot nem nyitható, az ügyfélterminál 134. hibát fog visszaküldeni, ha a nyitást mégis megkiséreljük. Hogy megtudjuk, az adott dealing centeren mennyi szabad marginra van szükség az adott szimbólumon 1 lot nagyságú megbízás megnyitásához, használjuk az egyszerű conditions.mq4scriptet: // // conditions.mq4 // The code should be used for educational purpose only. // int start() // Special function start Alert(Symbol()," Sell = ",AccountFreeMargin()// At selling -AccountFreeMarginCheck(Symbol(),OP_SELL,1)); Alert(Symbol()," Buy = ",AccountFreeMargin() // At buying -AccountFreeMarginCheck(Symbol(),OP_BUY,1)); return; // Exit start() //

179 Ez a kifejezés AccountFreeMargin() - AccountFreeMarginCheck(Symbol(),OP_SELL,1) lehetővé teszi, hogy kiszámítsuk a különbséget az elérhető szabad margin, és a megbízás feltételezett megnyitása után maradó szabad margin között. Ha olyankor indítjuk el a script végrehajtását, amikor tőzsdei megbízások nincsenek a terminálban, információt kapunk az 1 lot nagyságú buy illetve sell megbízás megnyitásához szükséges fedezet nagyságáról az adott szimbólumon: 89. ábra. 1-lot fedezet igénye különböző szimbólumokon a conditions.mq4 script alkalmazása alapján. Ha olyan szimbólum ablakában indítjuk el a conditions.mq4 scriptet ahol nyitott megbízások vannak, más értékeket fogunk kapni, a dealing center számítási módszerétől függően. Egyéb hibák és a MarketInfo() függvény Vannak további korlátozások az OrderSend() függvény paramétereinek meghatározásakor. Ez a maximális és a minimális megbízási árlépések, vagy a maximális és a minimális kötésméret értéke stb. A MarketInfo() függvény használata lehetővé teszi, hogy a szimbólumokkal kapcsolatos különféle információt kérdezzünk le az ügyféltermináltól. MarketInfo() függvény double MarketInfo(string symbol, int type) A függvény a szimbólumokkal kapcsolatos különféle információt küldi vissza, amiket az ügyfélterminál Market Watch ablakában láthatunk. Az aktuális szimbólummal kapcsolatos információkat előre definiált változókban tárolja a terminál. Paraméterek: symbol a szimbólum neve; type azonosító, amely meghatározza, hogy milyen fajta információt kérünk a függvénytől. Az azonosító értékek a következők lehetnek (lásd: MarketInfo függvény azonosítók). Néha változás történik a szerveroldalon. Például a gyorsan változó árak következtében a brókered növelheti azt a minimális távolságot, ami korlátozza a függőben levő megbízások és a stop megbízások elhelyezését. Később egy nyugodt piacnál a bróker megint csökkentheti ez a távolságot. Így néhány paraméter értékét bármikor megváltoztathatják. A ha azt akarod, hogy a programjaid stabilan működjenek, minimális legyen a visszautasított kérés, frissítened kell az információkörnyezet paramétereit a MarketInfo() és RefreshRates() függvényekkel, az OrderSend() függvény végrehajtása előtted

180 Példa egy olyan egyszerű scriptre, ami egy Buy megbízást ad a szabad margin 35% százalékával és előre beállított stop szintekel (openbuy.mq4). // // openbuy.mq4 // The code should be used for educational purpose only. // int start() // Special function start int Dist_SL =10; // Preset SL (pt) int Dist_TP =3; // Preset TP (pt) double Prots=0.35; // Percentage of free margin string Symb=Symbol(); // Symbol // while(true) // Cycle that opens an order int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Min. distance double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Min. volume double Step =MarketInfo(Symb,MODE_LOTSTEP);//Step to change lots double Free =AccountFreeMargin(); // Free Margin double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Cost per 1 lot // double Lot=MathFloor(Free*ProtsOne_LotStep)*Step;// Lots if (Lot < Min_Lot) // If it is less than allowed Alert(" Not enough money for ", Min_Lot," lots"); break; // Exit cycle // if (Dist_SL < Min_Dist) // If it is less than allowed Dist_SL=Min_Dist; // Set the allowed Alert(" Increased the distance of SL = ",Dist_SL," pt"); double SL=Bid - Dist_SL*Point; // Requested price of SL // if (Dist_TP < Min_Dist) // If it is less than allowed Dist_TP=Min_Dist; // Set the allowed Alert(" Increased the distance of TP = ",Dist_TP," pt"); double TP=Bid + Dist_TP*Point; // Requested price of TP // Alert("The request was sent to the server. Waiting for reply.."); int ticket=ordersend(symb, OP_BUY, Lot, Ask, 2, SL, TP); // if (ticket>0) // Got it!:) Alert ("Opened order Buy ",ticket); break; // Exit cycle // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 135:Alert("The price has changed. Retrying.."); RefreshRates(); // Update data continue; // At the next iteration case 136:Alert("No prices. Waiting for a new tick.."); while(refreshrates()==false) // Up to a new tick Sleep(1); // Cycle delay continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution RefreshRates(); // Update data

181 continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 5 : Alert("Outdated version of the client terminal."); break; // Exit 'switch' case 64: Alert("The account is blocked."); break; // Exit 'switch' case 133:Alert("Trading forbidden"); break; // Exit 'switch' default: Alert("Occurred error ",Error);// Other alternatives break; // Exit cycle // Alert ("The script has completed its operations "); return; // Exit start() // A script a start() különleges függvényből áll (1-10 blokkok). Az 1-2 blokkokban a megbízások nyitási értékeit állítjuk be. A 2-9 blokk egy while() ciklusoperátor, amiben minden szükséges számítást végrehajtunk. Ez a ciklus a kódban végrehajtja a megbízás nyitását. A 2-3 blokkban az információskörnyezet változóit frissítjü. A blokkokban a lot méretet és a stop szinteket számoljuk ki. A blokkban a hibafeldolgozást végezzük. A 9-10 blokkban, a szükséges üzenet jelenítjük meg, majd a script befejezte a munkáját. Vizsgáljuk meg a programkód néhány jellemzőjét! Könnyű észrevenni, hogy a kereskedelmi kérést a 6-7 blokkban alakítjuk ki. A 3-4 blokkban kiszámoljuk a lot méretét. Itt megvizsgáljuk, hogy az elérhető szabad margin elegendő-e megbízás nyitásához. Ha a 3-4 blokkban kiderül, hogy kevés a fedezet akkor a megfelelő üzenet megjelenítésével a break' operátor kilép a 2-9 ciklusból. Ha a vezérlés átkerül a 9-10 blokkba a script befejezi a működését. Az üzenetek a 9. blokkban fölöslegesek. Azok csak a felhasználót segítik, hogy milyen folyamatok zajlanak a scriptben, - mikor van a programnak vége, és a késedelem oka a hálózatban vagy a szerveren van. Ha a szabad margin elegendő a megbízás nyitásához, a vezérlés lekerül a 4-5 blokkhoz majd az 5-6 blokkhoz. Ezekben a blokkokban cikluskijárat nincs. Az 1-2 blokkban a TP-t szándékosan 3 pontra választottuk. A brókerek többsége 5 pontra állítja be a minimális távolságot. Az 5-6 blokkban a program fel fogja fedezni, hogy az előre beállított érték kevesebb a megengedettnél. A program olyan TP értéket fog beállítani, ami nem mond ellent a korlátozásnak. Azután a vezérlés a 6-7 blokkhoz kerül, hogy nyissa meg a megbízást. A blokk első sora üzenetet küld a felhasználónak. A kereskedelmi kérést csak azután küldjük el. Felmerül egy kérdés: Miért jelenítjük meg az üzenetet azelőtt, mielőtt a kereskedelmi kérést elküldjük? Először adhatnánk az utasítást, majd azután tájékoztathatnánk erről a felhasználót. A válasz erre a kérdésre szorosan összefügg azzal a technológiával, ahogy a kérést elküldjük az ügyfélterminálnak és azután a szervernek (lásd a 66. ábrát). Ez esetben a kereskedelmi kérést az OrderSend() függvényben egy értékadó operátor jobb oldalán alakítjuk ki. A így megalkotott kereskedelmi kérés ebből a függvényből küldjük a szerverre, és az értékadó operátor végre fog hajtódni, miután a szerver a kérés sorsáról visszaküldte a választ. Így, az egyetlen lehetőség tájékoztatni a felhasználót a kereskedelmi kérés küldéséről az, hogy az értékadó operátor előtt küldjük az üzenetet arról az eseményről, ami az értékadó operátor jobb oldalán a kereskedelmi függvényben történik. Előbb-utóbb az ügyfélterminál visszaküldi a vezérlést a programnak, a 6-7 blokkban a program az értékadó operátort végre fogja hajtani, aminek következtében a 'ticket' változó felvesz egy értéket, és a vezérlés tovább jut a hibaelemező blokkhoz. Ha a megbízás teljesül a szerveren, akkor a megbízás jegyszáma hozzárendelődik 'ticket' változóhoz. Ez azt jelenti, hogy a script teljesítette a feladatát és a programnak nem szükséges tovább futnia. A 7-8 blokkban lévő 'break' operátor kilép a while() ciklusból. A vezérlés eljut a 9-10 blokkba (a cikluson kívül), és a program befejeződik. Azonban, ha a kísérlet, hogy megnyissuk a megbízást nem sikerül, akkor a vezérlés a 8-9. hibaelemező blokkba kerül. Két hibacsoport létezik: azok ahol még van remény a megbízás sikeres megnyitására és azok, amelyek a programvégrehajtás egyértelmű végződéséhez vezetnek. Az 'Error' változó ebben az

182 esetben az utolsó hiba kódját kapja, azét a hibáét, amit az OrderSend() függvény végrehajtásánál visszaküldött a szerver vagy az ügyfélterminál. A 8-9 blokk első 'switch operátorában az kiküszöbölhető hibákat soroljuk fel. Ebben a csoportban minden hibát különbözőképpen kezelünk. Például, ha az ár változott (hiba 135) elegendő, ha frissítjük a környezeti paramétereket a RefreshRates() függvény használatával és ismét megkíséreljük, hogy nyissuk a megbízást. Ha a "No prices" (nincsenek árak) (hiba 136) hiba történik, nincs értelem kérést küldi a kereskedelmi szervernek. Ebben az esetben várnunk kell egy új tickre (addig a szerveren nincsenek elérhető árak) és azután újra próbálkozhatunk a megbízás nyitásával. Ezért ott egy várakozó ciklus kell elhelyezni a blokkban, a 136. hiba elhárításához. Ez a várakozó ciklus megszakad, amint egy új tick érkezik. Kilépünk a switch() operátorból a 'continue' operátorral, amely megszakítja a while() ciklus aktuális ismétlését és kezd egy újat. Kritikus hibákat más módon dolgozzuk fel. Ha egy ilyen hiba történik, a program értesíti a felhasználót és befejezi a munkáját. Ezért használjuk a 'break' operátort (az utolsó a 8-9 blokkban) amely megszakítja a while() és ez a program végződésével jár. Meg kell jegyeznünk, hogy ebbe a példába szándékosan nem foglaltunk bele mindent hibát. Ebben az esetben nem az a szándékunk, hogy ellássuk a felhasználót egy kész programmal. Nagyon fontos, hogy a programozó saját maga elemezze a hibákat, és önállóan döntse el, hogy hogyan dolgozza fel a hibákat a programban. Ugyanakkor, néhány hibát nem lehet feldolgozni, mert a program úgy van felépítve, hogy nem érzékeli ezeket, például ebben az esetben a 129 és 130 hibák. A fenti példában van egy olyan kis algoritmikus hiba, amit nem találhatunk meg sem a fordításnál sem az ügyfélterminálban, sem a szerveren. Kritikus fontosságú minden csipetnyi kódrészlet a hatókörétől függetlenül. A kód a 4-5 blokkban: // if (Dist_SL<Min_Dist) // If it is less than allowed. Dist_SL=Min_Dist; // Set the allowed Alert(" Increased the distance of SL = ",Dist_SL," pt"); double SL = Bid - Dist_SL*Point; // Requested price of SL // A számítások során az if() operátor törzsében a Dist_SL változó új értéket kap. Tételezzük fel, hogy ez szokványos minimális távolság: 5 pont. Az első végrehajtásnál (gyors piacon), ezt az értéket a szerveren 20 pontra módosítják. A Min_Dist változó meg fogja kapni a 20-as értéket. int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimum distance Most tegyük fel, hogy a kereskedelmi kérést 136. hiba miatt utasították vissza. A program a 8-9 blokkban fogja követni az új ticket. Ebben az időszakban a szerver megváltoztathatja a minimális távolság értékét, például azt 10 pontra csökkenti. Amikor az új tick megérkezik az vezérlés átkerül az új ciklusba, és a Min_Dist változó új értéke egyenlő lesz 10-zel. Azonban a Dist_SL változó értéke változatlanul 20 marad (a 4-5 blokk kódja alapján a Dist_SL értéke csak növekedhet). Azért hogy kizárd ezt az algoritmikus hibát, a 4-5 blokkot olyan módon kell átírni, ahol csak az az érték változna meg ami a körülményektől függ, (ebben az esetben ez SL értéke), míg Dist_SL értéke nem változna, például így: // double SL = Bid - Dist_SL*Point; // Requested price of SL if (Dist_SL<Min_Dist) // If it is less than allowed SL = Bid - Min_Dist*Point; // Requested price of SL Alert(" Increased the distance of SL = ",Min_Dist," pt"); // Hasonló változtatást kell csinálni 5-6 blokkban a másik stop megbízással

183 Függőben levő megbízások adása Alapvető különbség a függőben levő, és a piaci megbízások elhelyezése között a programozásban nincs. Meg kell jegyezned azonban a tényt, hogy a függőben lévő megbízások elhelyezésekor és módosításakor a rendelkezésre álló fedezetet nem ellenőrzi sem az ügyfélterminál sem a szerver. Itt nincs korlátozás. A számlán rendelkezésre álló összegnél nagyobb összegre is adható függőben levő megbízás. Ezek a megbízások határozatlan ideig a szerveren maradhatnak. Amikor a piaci ár eléri a függőben levő megbízás kért nyitó árát a szerver akkor végez ellenőrzést. Ha van elegendő pénz a számlán, akkor nyitja ezt a megbízást, vagyis az piaci megbízássá válik. Ha nincs fedezet, akkor azt törölni fogja. WindowPriceOnDropped() függvény Az MQL4-ben van egy nagyon hasznos lehetőség programozási módszerrel meg tudjuk határozni a szimbólumablakban annak a helynek a koordinátáit ahová egy Expert Advisort vagy egy scriptet elhelyeztünk, mikor az egérrel hozzácsatoltuk őket az ablakhoz. Például meg tudjuk szerezni azt a koordináta értékét, ahová a scriptet áthúztuk, ha használjuk a WindowPriceOnDropped() függvényt. double WindowPriceOnDropped() A függvény visszaküldi az chart azon pontján lévő ár értékét, ahová az EA-t vagy a scriptet leejtettük. Az érték csak akkor lesz igaz, ha az EA-t vagy a scriptet az egérrel helyeztük el ('drag and drop'). Ezt az értéket nem határozhatjuk meg egyéni indikátorok csatolásánál. Példa egy olyan egyszerű scriptre, ami elhelyez egy BuyStop megbízást a szabad margin 35%-ával és előre beállított stop szintekkel (openbuystop.mq4). // // openbuystop.mq4 // The code should be used for educational purpose only. // int start() // Special function start int Dist_SL =10; // Preset SL (pt) int Dist_TP =3; // Preset TP (pt) double Prots=0.35; // Percentage of free margin string Symb=Symbol(); // Symbol double Win_Price=WindowPriceOnDropped(); // The script is dropped here Alert("The price is set by the mouse as Price = ",Win_Price);// Set by the mouse // while(true) // Cycle that opens an order int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Min. distance double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Min. volume double Free =AccountFreeMargin(); // Free Margin double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//Cost per 1 lot double Lot=MathFloor(Free*ProtsOne_LotMin_Lot)*Min_Lot;// Lots // double Price=Win_Price; // The price is set by the mouse if (NormalizeDouble(Price,Digits)< // If it is less than allowed NormalizeDouble(Ask+Min_Dist*Point,Digits)) // For BuyStop only! Price=Ask+Min_Dist*Point; // No closer Alert("Changed the requested price: Price = ",Price);

184 // double SL=Price - Dist_SL*Point; // Requested price of SL if (Dist_SL < Min_Dist) // If it is less than allowed SL=Price - Min_Dist*Point; // Requested price of SL Alert(" Increased the distance of SL = ",Min_Dist," pt"); // double TP=Price + Dist_TP*Point; // Requested price of TP if (Dist_TP < Min_Dist) // If it is less than allowed TP=Price + Min_Dist*Point; // Requested price of TP Alert(" Increased the distance of TP = ",Min_Dist," pt"); // Alert("The request was sent to the server. Waiting for reply.."); int ticket=ordersend(symb, OP_BUYSTOP, Lot, Price, 0, SL, TP); // if (ticket>0) // Got it!:) Alert ("Placed order BuyStop ",ticket); break; // Exit cycle // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 129:Alert("Invalid price. Retrying.."); RefreshRates(); // Update data continue; // At the next iteration case 135:Alert("The price has changed. Retrying.."); RefreshRates(); // Update data continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution RefreshRates(); // Update data continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 5 : Alert("Outdated version of the client terminal."); break; // Exit 'switch' case 64: Alert("The account is blocked."); break; // Exit 'switch' case 133:Alert("Trading fobidden"); break; // Exit 'switch' default: Alert("Occurred error ",Error);// Other alternatives break; // Exit cycle // Alert ("The script has completed its operations "); return; // Exit start() // Az openbuystop.mq4 script szerkezete ugyanúgy épül fel, mint az openbuy.mq4 script, tehát nem szükséges részletesen leírni azt. Csak a programok közti alapvető különbségekre fogjuk fordítani a figyelmünket. Az ár szintjét ahol a scriptet hozzáerősítettük a szimbólumablakhoz, a következő sor határozza meg: double Win_Price=WindowPriceOnDropped(); // A script is dropped here

185 Ennek a változónak az értékét végig megtartjuk a program működése alatt. Ez azért szükséges, hogy a kért nyitó ár a későbbiekben ne változzon. A script minden alkalommal ahhoz az árhoz viszonyítva számítja ki az kért ár értékei, ahol a felhasználó csatolta a scriptet. Könnyű belátni az openbuystop.mq4 script alapján, hogy itt nem vizsgáljuk a fedezet meglétét, a szabad margint a 3-4. blokkban elemezzük. Ha az ár kiszámított értéke nem felel meg a függőben levő Stop megbízás elhelyezése a követelményeinek ( lásd: A megbízások jellemzői és a kereskedés végrehajtás szabályai, Követelmények és korlátozások a kereskedelemben), akkor az értékét újraszámoljuk. A hibafeldolgozó blokkban szintén van néhány apró változtatás: néhány hibával nem foglalkozunk, de néhány új hibakódot feldolgozunk. Ésszerű korlátozások A kereskedelmi függvények használatával összefüggésben figyelembe kell venni néhány általános korlátozást. Például, a 146. hiba csak fordul elő, ha több program ad kereskedelmi kérést ugyanazon szimbólum különböző ablakaiban. A véleményünk szerint ez a gyakorlat megengedett, de nem ésszerű. Sokkal hatékonyabb egy olyan kereskedő programot létrehozni és használni, ami feldolgozza a kereskedelem minden sajátosságát. Ha csak egy kereskedő programot használunk, akkor lehetetlen ellentétes kereskedelmi kéréseket küldeni egyidejűleg. Azonfelül, az algoritmus sokkal jobban szervezett lehet egy ilyen programban: elemzi a sikeres kereskedés valószínűségét és eszerint a valószínűség szerint osztja el a rendelkezésre álló fedezetet. A kereskedés végrehajtásához hatékonyabb egy teljes értékű Expert Advisort használni, míg a scripteket csak egyszeri számításokra vagy hasznos információk megjelenítésére ajánlott alkalmazni. Ugyanakkor, ha a kereskedő nem használ Expert Advisort az automatizált kereskedelemre, a scriptek használatáról kiderül, hogy sokkal hatékonyabban működnek, mint az ügyfélterminál vezérlőpultja

186 Megbízások zárása és törlése Megbízások bezárása A megbízások befejezése iránti kereskedelmi kérések küldésére az OrderClose() függvényt használjuk. OrderClose() függvény bool OrderClose (int ticket, double lots, double price, int slippage, color Color=CLR_NONE) Ezt a függvényt használjuk a megbízások bezárására. A függvény a TRUE értéket küldi vissza ha a kérés végrehajtása sikeres. És visszaküldi a FALSE-ot, ha a végrehajtás nem sikerül. Paraméterek: ticket - a megbízás egyedi sorszáma. lots a bezárandó összeg. Megengedett, a nyitott megbízás összegénél kevesebbet, vagyis részlegesen bezárni egy megbízást. Ebben az esetben, ha a kereskedelmi kérést sikeresen végrehajtják, a megbízás részben lesz lezárva. price záró ár. Ezt a paramétert azon követelmények és korlátozások szerint állítjuk be, amiket korábban már megismertünk (lásd: A megbízások jellemzői és a kereskedés végrehajtás szabályai és 3. függelék). Ha a kért ár nem létezik, ezt a kereskedelmi kérést vissza fogják utasítani. Ha a piaci ár eltér a kért ártól, de ez az eltérése a csúszás (slippage) értékén belüli van, a kereskedelmi kérést az ügyfélterminál el fogja fogadni és tovább fogja küldeni a kereskedelmi szervernek. slippage - a kért árának a piaci ártól való eltérésének maximális megengedett értéke (pontokban), amikor még a megbízás teljesül. Color - az ábrán levő záró nyíl színe. Ha ez a paraméter nem létezik vagy az értéke CLR_NONE a nyíl nem fog megjelenni a charton. Ha a program a lezárandó megbízás fajtájáról, az egyedi azonosító számáról és lot-méretéről információval rendelkezik, akkor nagyon könnyű bezárni a megbízást. Ezért a programkódban az OrderClose() függvényhívást előre beállított paraméterekkel használjuk. Például, ha a Buy megbízás azonosító száma és 0.5 lot-ot akarsz bezárni, a függvényhívás, ami bezárja a megbízást, így néz ki: OrderClose( 12345, 0.5, Bid, 2 ); Hogy el tudjuk dönteni azt, melyik megbízásokat és milyen sorrendben zárjunk le, ismernünk kell az aktuális helyzetben nyitva lévő megbízások minden adatát. Az MQL4-ben sok függvényt használhatunk arra, hogy a megbízásokra jellemző különféle adatokat megkapjunk. Például, az OrderOpenPrice() függvény visszaküldi a megbízás nyitó árának az értékét (vagy a függőben levő megbízás kért árát), az OrderLots() függvény visszaküldi a lot értéket, az OrderType() függvény visszaküldi a megbízás fajtáját, stb. Minden adatlekérdező függvényt azon a megbízáson hajtunk végre, amit az OrderSelect() függvény kiválasztott. OrderSelect() függvény Ahhoz, hogy megkapjuk bármely megbízás paramétereit, (függetlenül attól, hogy piaci, függőben levő, lezárt vagy törölt), először ki kell választani azt az OrderSelect() függvénnyel. bool OrderSelect(int index, int select, int pool=mode_trades) OrderSelect egy olyan függvény, ami kiválaszt egy megbízást a további műveletek végrehajtására. Ez függvény visszaküldi a TRUE-t, ha sikeresen végrehajtották. Egyéb esetben visszaküldi a FALSE-ot. Paraméterek: index - a megbízás sorszáma vagy a jegyszáma, ez az érték a második paramétertől függ

187 select - a kiválasztási módszert jelző flag. Ennek a paraméternek a következő két lehetséges értéke lehet: SELECT_BY_POS - az index paraméterben a megbízás listabeli sorszámát küldjük el (a számozás 0-val kezdődik), SELECT_BY_TICKET - a index paraméterben a jegyszámot (ticket) küldjük el. pool - az adatforrás kiválasztása. A 'pool' paramétert akkor használjuk, mikor a 'select' paraméter értéke SELECT_BY_POS. A 'pool' paramétert figyelmen kívül hagyjuk, ha a megbízást jegyszám (ticket) alapján választjuk ki (SELECT_BY_TICKET). A pool' paraméternek két lehetséges értéke lehet: MODE_TRADES (alapértelmezett) - a megbízást a nyitott és függőben levő megbízások közül választjuk ki, azok közül, amelyek a Terminal ablak Kereskedés (Trade) fülére kattintva láthatók. MODE_HISTORY - a megbízást a lezárt és törölt megbízások közül választjuk ki, azok közül, amelyek a Terminal ablak Számlatörténet (Account History) fülére kattintva láthatók. Ebben az esetben a törölt és lezárt előzmények történelemének a mélységét a felhasználó határozza meg. A függvény használatának bemutatására a megbízások lezárásakor oldjunk meg egy feladatot: 28. feladat: Írj egy olyan scriptet, ami bezárja a rendelkezésre álló megbízások közül az egyiket. A script végrehajtásnak a scriptnek az egérrel a chartra dobási helyéhez legközelebbi megbízás befejezésével kell végződnie. Tegyük fel, hogy van három nyitott Eur/Usd megbízás, és egy függőben levő Usd/Chf megbízás a terminál a szimbólum ablakaiban: 90. ábra. Különböző megbízások különböző szimbólumokon a terminál ablakban. Egy olyan scriptet kell írnunk, amit, ha az egérrel a navigátor ablakból a szimbólumablakba húzunk, a megbízások közül bezárja azt, amelyik a kurzorhoz legközelebb van (abban a pillanat mikor a felhasználó felengedi az egérgombot). 91. ábrán azt az esetet látjuk ahol a kurzor a számú Sell megbízáshoz van a legközelebb. Ez az a megbízás, amit a script végrehajtása során zárni kell. 91.ábra. A closeorder.mq4 script, amit a kiválasztott megbízás bezárására használunk

188 A feladat megoldásához ki kell válogatni (az OrderSymbol() függvény segítségével) az összes közül azokat a megbízásokat, amelyeket azon a szimbólumon nyitottak, amely szimbólum ablakába a scriptet leejtjük. Ezután meg kell szereznünk minden kiválasztott megbízás nyitó árát (az OrderOpenPrice() függvényt végrehajtva egymás után mindegyik megbízáson). A megbízás nyitó árainak ismeretében könnyen ki tudjuk választani közülük azt amelyik megfelel a problémafelvetésnek. Azért, hogy az OrderClose() függvényben a megbízás többi paraméterét is meg tudjuk adni tudnunk kell néhány más adatot is a kiválasztott megbízásról: a lot összegét (az OrderLots() függvénnyel), a megbízás jegyszámát (ticket), (az OrderTicket() függvénnyel). Azonkívül, hogy a kettős ár helyes értékét alkalmazzuk a záráshoz, tudnunk kell a megbízás fajtáját (az OrderType() függvénnyel). Nézzük meg az OrderSelect() függvénynek milyen paramétereket kell megadnunk a fenti megbízás jellemzőinek a megszerezéséhez! Először is meg kell választani a megbízás kiválasztási módszerét. A feladatban a kiválasztási módszert a feladat felvetéskor határoztuk meg: A megbízások száma a script indításakor nem ismert, ezért a programnak tartalmaznia kell egy olyan blokkot, ami megállapítja a megbízások számát. Ez azt jelenti, hogy ellenőriznünk kell minden egyes megbízást, ami a Terminal ablakban található, (64.1. ábra), ezért nekünk a SELECT_BY_POS paramétert kell használnunk. Szintén fontos a vizsgált megbízások csoportjának kiválasztása. A probléma megoldásához nem szükséges a lezárt és a törölt megbízásokat elemezni. Ebben az esetben csak a nyitott és függőben lévő megbízásokat vizsgáljuk, ezért az OrderSelect() függvényben a MODE_TRADES paramétert használjuk. A 'pool' paraméter alapértelmezett értéke a MODE_TRADES ezért ennek a megadását mellőzhetjük a függvény fejlécben. Lent bemutatjuk, hogy hogyan épül föl a piaci és függőben levő megbízásokat elemző blokk: for (int i=1; i<=orderstotal(); i++) //Cycle for all orders.. //displayed in the terminal if(orderselect(i-1,select_by_pos)==true)//if there is the next one // Order characteristics.. //..must be analyzed here //End of the cycle body A ciklusoperátor fejlécében a kezdőértéket i=1 míg a cikluskijárathoz tartozó feltétel az i<=orderstotal() kifejezés. Az OrdersTotal() függvény visszaküldi a piac és függőben levő megbízások számát, azon megbízásokét, amelyeket a Terminal ablak Kereskedés fülére kattintva látunk. Ezért ezek a megbízások vesznek részt a ciklusok ismétléseiben. Az 'if' operátor ismétléseinek kiszámolásakor, az ismétléseket az OrderSelect(i-1,SELECT_BY_POS) paraméterekkel végezzük. A következő fontos dolgot szem előtt kell tartani: A piac és függőben levő megbízások listájában a megbízások számozása nullával kezdődik. Ez azt jelenti, hogy a megbízás listában (90. ábra) az első megbízás sorszáma nulla a másod megbízásé 1 a harmadiké 2 stb, az OrderSelect() függvényhívásban az index értékét i-1-nek adjuk meg. Ezért minden kiválasztott megbízásnál ez az index mindig az i változó értékénél 1-el kevesebb lesz ( a következő ismétlés sorszámával egyezik meg). Az OrderSelect() függvény visszatérési értéke true, ha a megbízást sikerült kiválasztani. Lehetséges, hogy a megbízás kiválasztás nem sikerül. Ez akkor történhet, ha a megbízások száma megváltozott a feldolgozás alatt. Amikor MQL4-ben programozol, figyelembe kell venned, hogy az alkalmazási program valós idejű módban fog működni és amíg feldolgoz valamely paramétert, ezen paraméter értéke megváltozhat. Például valamelyik megbízás megváltozhat, kinyílhat/bezárulhat a piac változása miatt. Ezért kell betartani a következő szabályt a megbízások feldolgozásának programozása során: A megbízásokat amint lehet, fel kell dolgozni, és az a blokk ahol a megbízások feldolgozása történik, ne tartalmazzon fölösleges programsorokat. Az a kód, ami az 'if 'operátor fejlécében található, ha a program megállapítja hogy a megbízás-listában a következő megbízás létezik, akkor azt kiválasztja. Ha a következő megbízás elérhető, a vezérlést le fogja küldeni az operátortörzsbe a megbízás paramétereinek feldolgozása céljából. Figyelembe kell venni azonban, hogy az ilyen programszerkezet nem sokat segít a lehetséges konfliktusok esetében, mert a

189 megbízás elveszhetett (bezárulhat) a paraméterek feldolgozása alatt. Azonban még mindig ez a leghatékonyabb megoldás abban az esetben, ha a kiválasztása pillanatában a megbízás már nem elérhető. Az 'if' operátor törzsében a kiválasztott megbízás paramétereit elemzzük. Mikor végrehajtjuk az OrderOpenPrice(), OrderTicket() és OrderType() függvényeket mindegyikük vissza fogja küldeni annak a megbízásnak egy bizonyos jellemzők értékét, amit az OrderSelect() függvény végrehajtásával kiválasztottunk. A fenti elvet használjuk abban a programban, ami megoldja 28. feladatot. Egy példa egy egyszerű scriptre, ami annak a megbízásnak a bezárására szolgál, amelyiknek a nyitó ára a legközelebb van ahhoz az árhoz, ahová a scriptet az egérrel elhelyeztük (closeorder.mq4). // // closeorder.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' string Symb=Symbol(); // Symbol double Dist= ; // Presetting int Real_Order=-1; // No market orders yet double Win_Price=WindowPriceOnDropped(); // The script is dropped here // for(int i=1; i<=orderstotal(); i++) // Order searching cycle if (OrderSelect(i-1,SELECT_BY_POS)==true) // If the next is available // Order analysis: // if (OrderSymbol()!= Symb) continue; // Symbol is not ours int Tip=OrderType(); // Order type if (Tip>1) continue; // Pending order // double Price=OrderOpenPrice(); // Order price if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)< //Selection NormalizeDouble(Dist,Digits)) // of the closest order Dist=MathAbs(Price-Win_Price); // New value Real_Order=Tip; // Market order available int Ticket=OrderTicket(); // Order ticket double Lot=OrderLots(); // Amount of lots // //End of order analysis //End of order searching // while(true) // Order closing cycle if (Real_Order==-1) // If no market orders available Alert("For ",Symb," no market orders available"); break; // Exit closing cycle // switch(real_order) // By order type case 0: double Price_Cls=Bid; // Order Buy string Text="Buy "; // Text for Buy break; // Из switch case 1: Price_Cls=Ask; // Order Sell

190 Text="Sell "; // Text for Sell Alert("Attempt to close ",Text," ",Ticket,". Awaiting response.."); bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// Order closing // if (Ans==true) // Got it! :) Alert ("Closed order ",Text," ",Ticket); break; // Exit closing cycle // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 135:Alert("The price has changed. Retrying.."); RefreshRates(); // Update data continue; // At the next iteration case 136:Alert("No prices. Waiting for a new tick.."); while(refreshrates()==false) // To the new tick Sleep(1); // Cycle sleep continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution RefreshRates(); // Update data continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 5 : Alert("Old version of the client terminal."); break; // Exit 'switch' case 64: Alert("Account is blocked."); break; // Exit 'switch' case 133:Alert("Trading is prohibited"); break; // Exit 'switch' default: Alert("Occurred error ",Error);//Other alternatives break; // Exit closing cycle // Alert ("The script has finished operations "); return; // Exit start() // A closeorder.mq4 program egész kódja a különleges start() függvényben összpontosul. Az 1-2 blokkban a változkat inicializáljuk. A Dist változó a script csatolási helye és az ahhoz legközelebbi megbízás közötti távolság. A Real_Order változó egy olyan flag, ami jelzi, ha legalább egy megbízás elérhető az ügyfélterminálban, (nem negatív érték). A Win_Price változó az az ár, aminél a felhasználó csatolta a scriptet a szimbólumablakhoz. A 2-6 blokkban egy megbízást elemzünk: A rendelkezésre álló megbízások közül az egyiket kiválasztjuk bezárásra. A 6-10 blokk befejezi a műveletet és feldolgozza a hibák, amik a végrehajtás alatt történhettek. Kezdeti pillanatban, akkor amikor a felhasználó hozzáerősítette a scriptet a szimbólumablakhoz, a Win_Price változó értékét meghatározzuk az 1-2 blokkban, és a változó felveszi azt az ár értéket, ahol a felhasználó a scriptet a charthoz csatolta. Most meg kell találni azt a megbízást, amelyik a legközelebb van ehhez az értékhez. A 'for' ciklusban (2-6 blokk), a megbízásokat megvizsgáljuk. A 2-3 blokkban a program ellenőrzi, hogy van-e a Terminal következő sorában megbízás. Ha egy további megbízást talál, a vezérlést átadja az 'if' operátor törzsébe, ahol elemezi a megbízás jellemzőit. A 3-4 blokk a hibás szimbólumokon (nem a program végrehajtás szimbólumán) nyitott megbízásokat szűri ki. Ez esetben ez a megbízás a Usd/Chf megbízás. Az OrderSymbol() visszaküldi a kiválasztott megbízás szimbólumnevét. Ha ez a szimbólumnév más, mint amin a programot végrehajtjuk, az aktuális ismétlés megszakad és megakadályozzuk a más szimbólumokon nyitott megbízások feldolgozását. Ha a megbízásról kiderül,

191 hogy megfelelő szimbólumon nyílt, még egy ellenőrzést végre fogunk hajtani. A meghatározzuk a megbízás típusát az OrderType() függvénnyel (lásd: A megbízások típusai). Ha a megbízás típusának paramétere nagyobb mint 1 ez azt jelenti, hogy ez egy függőben lévőmegbízás. Ebben az esetben az aktuális ismétlést szintén félbeszakítjuk mert nem foglalkozunk a függőben levő megbízásokkal. A példánkban van egy olyan megbízásunk is amit egy másik szimbólumon kötöttünk, de azt már korábban kiszűrtük. Minden megbízás, ami sikeresen átmegy a 3-4 blokkon az piaci megbízás. A 4-5 blokk feladata az, hogy kiválasszon egyet azon megbízások közül amelyek sikeresen átjutottak az előző blokkokon. Ennek az előre definiált árhoz (a Win_Price változó) alegközelebbi megbízásnak kell lennie. A felhasználótól nem követeljük meg, hogy pontosan eltalálja a megbízást az egérkurzorral. Azt a megbízást, amelyik közelebb van a kurzorhozbármelyik másik megbízásnál a a script indításakor ki fogjuk választani. A megbízás nyitó árának megtalálásához az OrderOpenPrice() függvényt használjuk. Ha a távolság abszolút értéke, az aktuális megbízás ára és a kurzorár között kevesebb az előző megbízás ugyanazon értékénél, az aktuális megbízást ki fogjuk választani (csak a távolság abszolút értékét vizsgáljuk, azt nem, hogy a kurzor az ár alatt vagy fölött van). Ebben az esetben ezt a megbízást memorizálni fogja 'for' ciklus, lezárásra esélyes megbízásként. A lezárandó megbízás paraméterei ( a jegyszám, a lot méret és a típus) a 4-5 blokk végén kerülnek kiszámításra. Ebben a példában (90. ábra), a megbízások száma négy (három piaci és egy függőben levő megbízás), ezért négy for ciklusismétlés lesz, mely ismétlések végén, meg fogjuk kapni a bezárásra kiválasztott megbízás minden szükséges adatát. Ekkor a programban a vezérlés átkerül a while ciklusoperátorhoz (6-10 blokk). A 6-7 blokkban a megbízás létét ellenőrizzük. Ha a 2-4 blokkban megbízásokat nem találtunk(ez lehetséges eset), a Real_Order flag értéke -1 marad, ami a megbízások elérhetetlenségét jelzi. Ha a 6-7 blokkban a program észleli, hogy nincsenek megbízások, a 'while' ciklus végrehajtása megszakad, és a program befejezi a működését. Ha a Real_Order változó értéke egyenlő 0-val vagy 1-gyel, ez azt jelenti, hogy van olyan megbízás amit be kell zárni. A 7-8 blokkban a megbízás típusának megfelelő záróár meghatározása történik. Ez Buy megbízásnál a Bid értéke, és a Sell megbízásnál az Ask értéke (lásd: A megbízások jellemzői és a kereskedés végrehajtás szabályai ). A 7-8 blokkban a Text változó értékét is kiválasztjuk. A megbízás befejezése iránti kereskedelmi kérést az OrderClose() függvényben alakítjuk ki: bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// Order closing Az OrderClose() kereskedelmi függvény true értéket küld vissza, ha a kérést sikeresen végrehajtotta, és false-t, ha nem. Ha a kereskedelmi kérést sikeresen végrehajtják a szerveren, az Ans változó true értéket fog kapni. Ebben az esetben, 8-9 blokk végrehajtása után a program informálni fogja a felhasználót a megbízás sikeres befejezésről. Azután, a 'while' ciklusoperátor végrehajtása és ezzel a program is be fogj fejeződni. Egyéb esetben a vezérlés a 9-10 blokkba kerül hibaelemzés végett, majd visszakerül az ügyfélterminálhoz. A 9-10 blokk kezdetén a hibakódot határozzuk meg. Azután a hibakód szerint vagy kilépünk a programból, vagy a ciklust újból végrehajtjuk. Az első 'switch' operátorban, a program feldolgozza a helyrehozható hibákat, ezek a hibák ideiglenes nehézségként jelentek meg a kereskedelem végrehajtása során. Minden szükséges műveletet elvégzünk az egyes hibákon, azután az aktuális ismétlés megszakad és a 'while ciklus végrehajtása újraindul. Ha a hibakódot nem tudjuk feldolgozni az első 'switch' operátorban, kritikus hiba történt. Ebben az esetben a vezérlést átadjuk a második 'switch' operátornak, amit végrehajtva tájékoztatjuk a felhasználót arról, hogy kritikus hiba történt. Majd a program a 'break' operátort használja, ami félbeszakítja a 'while' ciklus végrehajtását. A 'return' operátor leállítja astart() különleges függvény végrehajtását és a program befejezi a munkáját. A script végrehajtásának eredményét, lent láthatjuk. (90. és 91. ábra) A kereskedés sikeresen megtörtént a szerveren

192 92. ábra. Az üzenetek a closeorder.mq4 script sikeres végrehajtásának eredményéről. A megbízás zárása után két megbízás maradt az Eur/Usd ablakában. 93. ábra. A closeorder.mq4 script végrehajtása az egyik megbízás zárásával végződik. A megbízás bezárását a Terminál ablakban is követhetjük: 94. ábra. A closeorder.mq4, script végrehajtása után két megbízást látunk a Terminál ablakban. Később a két másik megbízást is lezártuk a script újbóli használatával. Függőben levő megbízások törlése Kereskedelmi kéréseket adhatunk a függőben levő megbízások törlésére az OrderDelete () függvénnyel. OrderDelete() függvény bool OrderDelete(int ticket, color arrow_color=clr_none) A függvény törli a korábban elhelyezett függőben levő megbízást. TRUE-t küld vissza, ha ez sikeresen megtörtént. Ha nem akkor FALSE-ot küld vissza. Paraméterek:

193 ticket - a megbízás egyedi száma. arrow_color - az ábrán levő nyíl színe. Ha ez a paraméter nincs, vagy az értéke CLR_NONE, a nyilat, nem fogjuk látni az ábrában. Könnyű észrevenni, hogy az OrderDelete( ) függvény nem tartalmazza a mennyiséget és a törlendő megbízás árát. A megbízás mindennek ellenére törlödik bármilyen piaci árak. Egy megbízás részbeni törlése lehetetlen. Két lépesben lehet csökkenteni a függőben levő megbízások méretét: törölni a létező megbízást, azután egy új függőben levő megbízást adni kisebb lot összeggel. Annak a programnak az algoritmusa, ami törölni fog egy függőben levő megbízást, nagyon hasonlít a megbízást záró programhoz. Egy csekély különbség abban áll, hogy a záró árat nem kell megadni egy függőben levő megbízás törléséhez, ezért a lenti program nem tartalmazza azt a blokkot, ami piaci árakat frissít. Egy példa egy egyszerű scriptre, aminek a feladata annak a függőben levő megbízásnak a törlése, aminek a beállított nyitó ára a legközelebb van a script elhelyezésének helyszínéhez (deleteorder.mq4). // // deleteorder.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' string Symb=Symbol(); // Symbol double Dist= ; // Presetting int Limit_Stop=-1; // No pending orders yet double Win_Price=WindowPriceOnDropped(); // The script is dropped here // for(int i=1; i<=orderstotal(); i++) // Order searching cycle if (OrderSelect(i-1,SELECT_BY_POS)==true) // If the next is available // Order analysis: // if (OrderSymbol()!= Symb) continue; // Symbol is not ours int Tip=OrderType(); // Order type if (Tip>2) continue; // Market order // double Price=OrderOpenPrice(); // Order price if (NormalizeDouble(MathAbs(Price-Win_Price),Digits)> //Selection NormalizeDouble(Dist,Digits)) // of the closest order Dist=MathAbs(Price-Win_Price); // New value Limit_Stop=Tip; // Pending order available int Ticket=OrderTicket(); // Order ticket // End of 'if' //End of order analysis // End of order searching // switch(limit_stop) // By order type case 2: string Text= "BuyLimit "; // Text for BuyLimit break; // Exit 'switch' case 3: Text= "SellLimit "; // Text for SellLimit break; // Exit 'switch' case 4: Text= "BuyStopt "; // Text for BuyStopt break; // Exit 'switch'

194 case 5: Text= "SellStop "; // Text for SellStop break; // Exit 'switch' // while(true) // Order closing cycle if (Limit_Stop==-1) // If no pending orders available Alert("For ",Symb," no pending orders available"); break; // Exit closing cycle // Alert("Attempt to delete ",Text," ",Ticket,". Awaiting response.."); bool Ans=OrderDelete(Ticket); // Deletion of the order // if (Ans==true) // Got it! :) Alert ("Deleted order ",Text," ",Ticket); break; // Exit closing cycle // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 4: Alert("Trade server is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 137:Alert("Broker is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 64: Alert("Account is blocked."); break; // Exit 'switch' case 133:Alert("Trading is prohibited"); break; // Exit 'switch' case 139:Alert("The order is blocked and is being processed"); break; // Exit 'switch' case 145:Alert("Modification is prohibited. ", "The order is too close to the market"); break; // Exit 'switch' default: Alert("Occurred error ",Error);//Other alternatives break; // Exit closing cycle // Alert ("The script has finished operations "); return; // Exit start() // A hibafeldolgozó blokk kissé megváltozott. A piaci megbízások zárásakor a végrehajtás alatti árváltozás lehetőségét szem előtt kell tartani, (135 és 136 hibák) de ilyen hibák nem történnek függőben levő megbízások törlésekor. Ezért a RefreshRates() függvényt sehol nem használjuk a programban. Bizonyos hibák, például: 4. és 137. hiba feldolgozása (lásd: Hibakódok) ) kicsit nehezebb. Például, mikor 137 hibát kapunk a program figyelembe veszi, hogy a bróker elfoglalt. Azonban egy természetes kérdés felmerül: A bróker mikor szabadul föl, a felhasználó mikor folytathatja a kereskedést? A 137. hiba nem nyújt ilyen információt. Ezért a programozónak magának kell eldöntenie, hogy hogyan építse föl a programot, ami az ilyen hibákat feldolgozza. Egyszerű esetben a kérést egy bizonyos szünet után ismételhetik (a példánkban, 3 másodperc után). Másfelől, a sikertelen kereskedelmi kérések sorozata

195 után a szerver 141. hibát küldhet vissza - túl sok kérés. Ez a hiba ahhoz vezet, hogy a deleteorder.mq4 script befejezi a munkáját. Általában az ilyen konfliktusok megoldása nem programozási feladat. Ilyen esetben kapcsolatba kell lépni a dealing center supporttal és tisztázni kell az elutasítás okait és azt, hogy hogyan hajthatod végre a kereskedelmi kérést hiba akkor történik, ha egy függőben levő megbízás kért nyitó ára (ugyanaz a helyzet a nyitott megbízások stop áraival is) túl közel van a piaci árhoz. Ez a hiba nem történik meg, ha nyugodt piacon kereskedsz. Ha az árak gyorsan változnak, a brókered eldöntheti, hogy bizonyos megbízásokat, amelyek valószínűleg hamarosan teljesülni fognak enged-e módosítani vagy törölni. Ezt a hibát kritikus hibának vesszük a scriptben és ezért ez a program végződéséhez fog vezetni (nincs értelme, hogy kereskedelmi kérésekkel zaklassuk a brókert). Ha az ár egy idő után változik, próbáld meg törölni a megbízást azáltal, hogy megint elindítod a scriptet. Általában, a 145. hibát meg tudod akadályozni, ha a fagyott zónát, amit beállított a bróker figyelembe veszed. A fagyott zóna egy olyan érték, ami egy árszalagot határoz meg, amin belül a megbízás befagyott, vagyis tilos törölni azt. Például, ha egy függőben levő megbízást nál adtunk és a fagyott zóna egyenlő 10 ponttal, ez azt jelenti, ha piaci ár az tartományon belül van a függőben levő megbízás törlését megtiltják. A fagyott zóna értékét a MarketInfo() MODE_FREEZELEVEL függvényt végrehajtásával kérdezhetjük le. Ellentétes irányú megbízások bezárása Opposite (Counter) Order ellentétes irányú megbízás- egy olyan megbízás, amit egy másik megbízással ellentétes irányban nyitottak ugyanazon a szimbólumon. Ha van két ellentétes megbízás egy bizonyos szimbólumon, egyidejűleg le lehet zárni őket, az OrderCloseBy() függvény által. Ezzel a módszerrel az egyik spreadet meg lehet takarítani. OrderCloseBy() függvény bool OrderCloseBy(int ticket, int opposite, color Color=CLR_NONE) A függvény egy megbízást bezár egy másik megbízás által, amit ugyanazon a szimbólumon nyílt de ellentétes irányban. A függvény visszaküldi TRUE-t, ha azt sikeresen végrehajtja, és FALSE-ot, ha nem. Paraméterek: ticket - a lezárandó megbízás egyedi azonosító száma. opposite - a szemben lévő megbízás egyedi azonosító száma. Color - az ábrában levő záró nyíl színe. Ha ez a paraméter hiányzik vagy az értéke CLR_NONE, a nyil nem fog megjelenni a charton. Nem feltétel, hogy szemben lévő megbízások ugyanakkora méretűek legyenek. Ha egy megbízást egy másik, kisebb méretű szemben lévő megbízással zárunk be a megbízás a maradék mennyiséggel továbbra is fennmarad. Figyeljünk meg egy példát! Legyen az ügyfélterminálban két azonos nagyságú megbízás, az egyik Buy a másik Sell. Ha külön zárjuk be őket az OrderClose() függvénnyel, a profitok összege a következőképpen alakul:

196 95. ábra. A megbízások a különálló zárásának az eredménye, ha az OrderClose() függvényt használjuk. Ezzel szemben, ha erre a célra az OrderCloseBy() függvény használjuk, a profitunk kedvezőbb lesz (az előző alternatívával összehasonlítva) az egyik megbízás spreadjének értékével: 96. ábra. Két ellentétes megbízás kölcsönös bezárásának eredménye, ha az OrderCloseBy() függvényt használjuk. Nyilvánvaló, ha vannak ellentétes megbízások a terminálban, gazdaságilag előnyösebb az OrderCloseBy() függvényt használni és nem az OrderClose()-t. A megtakarítással kapcsolatban, amit a megbízások ilyen módszerrel történő bezárása eredményez, adnunk kell néhány általános magyarázatot. Ami azt illeti, egy megbízás megnyitása (például, egy Buy megbízás) implicite egy korábban nyitott ellentétes irányú (vagyis Sell megbízás) bezárásaként is felfogható. Más szóval gazdaságilag egyenértékű a két alternatíva használata: vagyis, hogy bezárunk egy megbízást vagy kinyitunk egy ellentétes megbízást (és azután egymással lezárjuk mindkét megbízást). A két alternatíva közti különbség csak a különböző módszerekben állhat, hogy a különböző dealing centerek hogy számolják a megbízásokhoz szükséges fedezetet (lásd: 85. és 88. ábrák). Szintén könnyű belátni, hogy a záró árat nem szükséges megadni az OrderCloseBy()függvényben az ellentétes megbízások zárásához. Ez fölösleges, mert a profitok és veszteségek kiegyenlítik egymást, ezért a teljes gazdasági végeredmény nem függ a piaci ártól. Természetesen ez a szabály csak azonos méretű megbízásokra igaz. Ha például nekünk két megbízásunk van egy szimbólumon: egy Buy megbízás 1lot és egy Sell megbízás 0.7 lot mérettel, a piaci ártól csak a 0.3 lotos Buy megbízásrész függ, míg a 0.7 lotos megbízásrész nem függ a szimbólum árától. A szemben lévő megbízások nem befolyásolják a teljes kereskedés eredményét. Ezért a azoknak a kereskedelmi taktikáknak, amelyek ellentétes megbízások nyitásán alapulnak nincs semmilyen informális tartalmuk (ezért néhány dealing center erőszakkal bezárja a szemben lévő megbízásokat a méretüket figyelembe véve). Az egyetlen (negatív) hatása az ilyen taktikáknak az, hogy a szabályok szerint a fedezethez szükséges pénz nagyobb lehet. Azonkívül több szemben lévő megbízás kezelése eggyel több

197 nehézséget jelent a kereskedés programozása során. Ha a különféle díjjak és swapokat megvizsgáljuk (mindegyik megbízásét külön - külön), a szemben fekvő megbízások bezárása nyilvánvalóvá válik. Egy példa egy olyan egyszerű scriptre, ami bezár minden ellentétes megbízást egy szimbólumon (closeby.mq4). // // closeby.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' string Symb=Symbol(); // Symbol double Dist= ; // Presetting // while(true) // Processing cycle.. //..of opposite orders double Hedg_Buy = -1.0; // Max. cost of Buy double Hedg_Sell= -1.0; // Max. cost of Sell for(int i=1; i<=orderstotal(); i++) // Order searching cycle if(orderselect(i-1,select_by_pos)==true)// If the next is available // Order analysis: // if (OrderSymbol()!= Symb) continue; // Symbol is not ours int Tip=OrderType(); // Order type if (Tip>1) continue; // Pending order // switch(tip) // By order type case 0: // Order Buy if (OrderLots()>Hedg_Buy) Hedg_Buy=OrderLots(); // Choose the max. cost int Ticket_Buy=OrderTicket();//Order ticket break; // From switch case 1: // Order Sell if (OrderLots()>Hedg_Sell) Hedg_Sell=OrderLots(); // Choose the max. cost int Ticket_Sell=OrderTicket();//Order ticket //End of 'switch' //End of order analysis //End of order searching // if (Hedg_Buy<0 Hedg_Sell<0) // If no order available.. //..of some type Alert("All opposite orders are closed :)");// Message return; // Exit start() // while(true) // Closing cycle // Alert("Attempt to close by. Awaiting response.."); bool Ans=OrderCloseBy(Ticket_Buy,Ticket_Sell);// Закрытие // if (Ans==true) // Got it! :) Alert ("Performed closing by."); break; // Exit closing cycle // int Error=GetLastError(); // Failed :(

198 switch(error) // Overcomable errors case 4: Alert("Trade server is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 137:Alert("Broker is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 64: Alert("Account is blocked."); break; // Exit 'switch' case 133:Alert("Trading is prohibited"); break; // Exit 'switch' case 139:Alert("The order is blocked and is being processed"); break; // Exit 'switch' case 145:Alert("Modification is prohibited. ", "The order is too close to market"); break; // Exit 'switch' default: Alert("Occurred error ",Error);//Other alternatives Alert ("The script has finished operations "); return; // Exit start() // End of the processing cycle // // End of start() // A fenti script algoritmusa az előzőektől kicsit eltérő. Ez a különbség abban áll, hogy ugyanazt a kódot kell végrehajtani többször egymás után, hogy több megbízást is bezárjunk, (a bezárandó megbízások száma nem korlátozott). A scriptet a megbízások véletlen készletén teszteltük. Az 5 különböző méretű megbízást a 97. ábrán láthatjuk. 97. ábra. Ugyanazon a szimbólumon nyitott piaci megbízások. Az ellentétes megbízások bezárása során meg kell határozni, hogy a bezárandó megbízások sorrendjét milyen kritériumok alapján választjuk ki. Ez a kritérium az adott algoritmusban a megbízás mérete - a nagyobb megbízásokat zárjuk először, azután pedig sorban a kisebbeket. Miután a szemben lévő megbízásokat bezártuk, töredék megbízások keletkeznek. Például az ellentétes Buy (1 lot) és Sell (0.8 lot) bezárása után Buy (0.2 lot) megbízás nyílik. Ezért minden sikeres végrehajtás után a programnak frissítenie kell a megbízások listáját, és ebben a frissített listában kell megkeresni a következő két legnagyobb ellentétes irányú megbízást. A fenti számításokat egy 'while' ciklusban valósítjuk meg (2-10 blokk). A ciklus kezdetén, mindegyik ismétlésnél a program azt feltételezi, hogy egy bizonyos típusú megbízás már nem létezik. Ezért, a -1 értéket kapják a Hedg_Buy és Hedg_Sell változók. A megbízás feldolgozási blokk algoritmusa változatlan (lásd a closeby.mq4 kódját). A megbízásokat kereső 'for' ciklus a 3-4 blokkban ugyanúgy, mint az előző programokban a rossz megbízásokat ki szűri. Ebben az esetben ezek a megbízások a másik szimbólumhoz tartozó és a függőben levő megbízások

199 A 4-5 blokkban a 3-4 blokkban megfelelt megbízások méretét kiszámoljuk (kötési irányonként). Ha a számítások alatt kiderül, hogy a jelenleg feldolgozott megbízás a vele egyirányú megbízások közül az eddigi legnagyobb a jegy számát (ticket) eltároljuk. Ez azt jelenti, hogy megtaláltuk egy olyan megbízás jegyszámát, aminek esélye van a bezárásra, ha megfelelő ellenirányú megbízást találunk. Mikor a 'for' ciklus utolsó ismétlése véget ért, már ismerjük a két legnagyobb ellentétes irányú megbízást. Ezeket a megbízásokat kiválasztja a program. Ha már csak egyik irányú megbízásunk van, az 5-6 blokkból kilépünk a programból. A 6-10 blokkok a hibafeldolgozást végzik. Ez ugyanúgy történik, mint az előző esetekben (ebben és az előző részekben). A szemben lévő megbízások befejezése iránti kereskedelmi kérést a 7-8 blokkban alakítjuk ki, amire az OrderCloseBy() függvényt használjuk. Ha itt hiba történik, a hibakód alapján a program átadja a vezérlést a megfelelő helyre és újra próbálkozni a kereskedelem végrehajtásával (ugyanazokon a ticketeken) vagy a 'return' operátor befejezi a programot. Ha egy kereskedelmi kérelem sikeresen teljesült, a program kilép a hibafeldolgozó blokkból, és a legkülső 'while' ciklus aktuális ismétlése véget fog érni. Ennek a ciklusnak a következő ismétlésében, minden számítást megismétlünk: megbízások keresését, a rendelkezésre álló megbízások közül a két bezárandó ellentétes megbízás kiválasztását, a megbízások zárása iránti kereskedelmi kérés elküldését, és a hibaelemzést. Ezt a ciklust addig fogjuk végrehajtani, amíg elfogynak a megbízások (vagy az egyik vagy esetleg mindkét típus egyszerre). Ezt az eseményt az 5-6 blokkban érzékeljük, azután a program befejezi a működését. A következő üzeneteket kaptuk a closeby.mq4 script végrehajtása során, figyeld a 97. ábrát is: 98. ábra. Üzenetek a closeby.mq4 script végrehajtásakor. A Terminal Számlatörténet ablakában láthatod, hogy néhány megbízás nulla profittal lett lezárva. Ezek spreadjeit mentettünk meg, amikor a szemben lévő megbízásokat zártuk. A gazdasági eredményeket 97. és 99. ábrákon lehet összehasonlítani: 99. ábra. A closeby.mq4.script végrehajtása utáni számlatörténet

200 A Terminal ablakban a Napló fülön követni tudod a megbízások bezárásának történetét (a legújabb események fölül vannak): 100. ábra. A closeby.mq4 script végrehajtása alatt történt események. A script algoritmusának végrehajtása szerint a pillanatnyilag rendelkezésre álló legnagyobb megbízást zárjuk be először. Annak ellenére, hogy a megbízásokat véletlen sorozatban nyitottuk, (97. ábra), az első bezárt megbízások a Buy , 1lot mérettel és Sell , 0.8 lot mérettel (az alsó sorok a 100. ábrán). Mivel ezeknek a megbízásoknak különböző a méretük, a megbízások zárásakor keletkezett egy 0,2 lot nagyságú új megbízás: Buy Ezután a program kiválasztotta a Buy és Sell , 0.5 lotos megbízásokat. Ezek a megbízások bezárultak anélkül, hogy nyílt volna egy új megbízást. Amikor a harmadik ismétlés kezdődött a külső ciklusban két megbízás maradt a szimbólumablakban: az eredetileg is meglévő Sell lotos megbízás, és a script által nyitott Buy lotos megbízás ábra felső soraiból látható, hogy ezek az ellentétes megbízások szintén bezárultak. Ezeknek a mérete különböző volt, úgyhogy az utolsó kereskedelmi művelet egy 0.1 lotos megbízás nyitásával végződött, ami meg is maradt a szimbólumablakban (nézd meg a gazdasági eredményeket): 101. ábra. A megmaradó 0.1-lotos Sell megbízás. Célszerű a closeby.mq4 scriptet használni a kézi kereskedelemben, különösen akkor, ha sok ellentétes megbízásunk van a szimbólumablakban

201 A megbízások módosítása Az MQL4 lehetővé teszi a piaci és a függőben levő megbízások módosítását. A megbízásokat azon szabályok szerint módosíthatjuk, amelyeket a Megbízások jellemzői és a Követelmények és korlátozások a kereskedelemben fejezetekben leírtunk. OrderModify() függvény Az OrderModify() függvény segítségével piaci és függőben levő megbízásokat módosíthatunk. bool OrderModify(int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=clr_none) A függvény módosítja a piaci és függőben levő megbízások paramétereit. A függvény visszaküldi a TRUE értéket, ha a kérést sikeresen végrehajtotta. Különben a FALSE-ot küldi vissza. Paraméterek: ticket - a megbízás egyedi azonosító száma. price - egy függőben levő megbízás újonnan kért ára, vagy a megbízás nyitó ára. stoploss - a StopLoss új értéke. takeprofit a TakeProfit új értéke. expiration - függőben levő megbízás lejárati ideje. arrow_color - a módosított StopLoss és/vagy TakeProfit nyilak színe az ábrán. Ha ez a paraméter hozzáférhetetlen vagy az értéke CLR_NONE, a nyilakat nem fogják mutatni az ábrán. Megjegyzés: Csak a függőben levő megbízások esetén tudod a nyitó árat és a lejáratot megváltoztatni. Ha a függvényparaméterekként változatlan értékeket adsz meg, a terminál 1. hibakódot fog visszaküldeni (ERR_NO_RESULT). Van olyan szerver, ahol nem lehetséges a függőben levő megbízások lejárat idejét megadni. Ebben az esetben, ha megpróbálsz egy nem nullaértékű lejárati paramétert létrehozni, a 147. hibakódot (ERR_TRADE_EXPIRATION_DENIED) kapod válaszul. A megbízások módosítása Egy általános megbízás két stopmegbízást tartalmaz - StopLoss és TakeProfit. Ezek utasítást adnak a megbízás zárására adott árakon, azért hogy korlátozzuk a veszteséget és realizáljuk a profitot. A tőzsdei megbízások módosítása hasznos lehet akkor, ha meg kell változtatni a stop megbízások kért árait egy új értékre, a program vagy a kereskedő kezdeményezésére. Az ügyfélterminálnak van saját eszköze a StopLoss módosítására: a követő stop. Ez lehetővé teszi a programnak, hogy módosítsa a StopLoss szintjét, és az kövesse az aktuális árat egy bizonyos rögzített távolsággal. (lásd MetaTrader 4 Client Terminal User Guide-ot). Az OrderModify() megbízás-módosító függvény jelentősen kiterjeszti a módosítás lehetőségét: Mindkét stop megbízás kért árait megváltoztathatjuk vagy törölhetjük a piaci ár függvényében. A megbízások módosítását korlátozza a stop megbízások és a piaci ár közötti minimális megengedett távolság, amit a dealing center beállított. (lásd: Megbízások jellemzőiés a Követelmények és korlátozások a kereskedelemben). Ha a program megpróbál megváltoztatni egy stop pozíciót, egy olyan értékre, ami a megengedett minimális távolságnál közelebb van a piaci árhoz, az ilyen kereskedelmi kérés vissza fogja utasítani az ügyfélterminál és az OrderModify() függvényt nem hajtja végre, (130. hiba). Ezért a programba be kell építeni egy olyan blokkot, ami meg fogja vizsgálni ezt a korlátozást

202 Példa egy egyszerű Expert Advisorra, ami módosítja StopLoss megbízást, ha a távolság a StopLoss kért ára és a piaci ár között nagyobb, mint az előre beállított érték (modifystoploss.mq4) // // modifystoploss.mq4 // The code should be used for educational purpose only. // extern int Tral_Stop=10; // Trailing distance // int start() // Special function 'start' string Symb=Symbol(); // Symbol // for(int i=1; i<=orderstotal(); i++) // Cycle searching in orders if (OrderSelect(i-1,SELECT_BY_POS)==true) // If the next is available // Analysis of orders: int Tip=OrderType(); // Order type if(ordersymbol()!=symb Tip>1)continue;// The order is not "ours" double SL=OrderStopLoss(); // SL of the selected order // while(true) // Modification cycle double TS=Tral_Stop; // Initial value int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);//Min. distance if (TS < Min_Dist) // If less than allowed TS=Min_Dist; // New value of TS // bool Modify=false; // Not to be modified switch(tip) // By order type case 0 : // Order Buy if (NormalizeDouble(SL,Digits)< // If it is lower than we want NormalizeDouble(Bid-TS*Point,Digits)) SL=Bid-TS*Point; // then modify it string Text="Buy "; // Text for Buy Modify=true; // To be modified break; // Exit 'switch' case 1 : // Order Sell if (NormalizeDouble(SL,Digits)> // If it is higher than we want NormalizeDouble(Ask+TS*Point,Digits) NormalizeDouble(SL,Digits)==0)//or equal to zero SL=Ask+TS*Point; // then modify it Text="Sell "; // Text for Sell Modify=true; // To be modified // End of 'switch' if (Modify==false) // If it is not modified break; // Exit 'while' // double TP =OrderTakeProfit(); // TP of the selected order double Price =OrderOpenPrice(); // Price of the selected order int Ticket=OrderTicket(); // Ticket of the selected order Alert ("Modification ",Text,Ticket,". Awaiting response.."); bool Ans=OrderModify(Ticket,Price,SL,TP,0);//Modify it! //

203 if (Ans==true) // Got it! :) Alert ("Order ",Text,Ticket," is modified:)"); break; // From modification cycle. // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 130:Alert("Wrong stops. Retrying."); RefreshRates(); // Update data continue; // At the next iteration case 136:Alert("No prices. Waiting for a new tick.."); while(refreshrates()==false) // To the new tick Sleep(1); // Cycle delay continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying "); Sleep(500); // Simple solution RefreshRates(); // Update data continue; // At the next iteration // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 5 : Alert("Old version of the client terminal."); break; // Exit 'switch' case 64: Alert("Account is blocked."); break; // Exit 'switch' case 133:Alert("Trading is prohibited"); break; // Exit 'switch' default: Alert("Occurred error ",Error);//Other errors break; // From modification cycle // End of modification cycle // // End of order analysis // End of order search // return; // Exit start() // A fenti program egy Expert Advisor. Ha szükséges, könnyen meg tudod valósítani a megbízás módosító függvényt egy scriptben. Azonban, nem célszerű scriptet használni erre a feladatra, mert a script befejezné a munkáját, miután a kereskedelmi kérést végrehajtotta. Egy script használata csak egy olyan programban célszerű, ami egy egyszeri műveletet hajt végre, mint például egy megbízás kinyitás vagy bezárása. Ebben az esetben egy olyan feladatot kell megoldanunk, ahol folytonos irányításra van szükség: változtasd meg egy stop megbízás pozícióját, ha egy bizonyos feltétel teljesül, mégpedig ha a távolság a piaci ár és a stop megbízás értéke között meghalad egy bizonyos előre beállított értéket (ebben az esetben ez 10 pont). Folyamatos használatra sokkal kényelmesebb írni egy EA-t ami minden ticknél végrehajtódik a felhasználó közvetlen ellenőrzése nélkül. A fenti modifystoploss.mq4 EA algoritmusa nagyon egyszerű. A fő számításokat a megbízás kereső ciklusban végezzük (2-9 blokk). A megkeressük a nyitott megbízásokat, a piaci és függőben lévő megbízásokat is (az OrderSelect() függvényhívásban a 'pool' paraméter nincs megadva). A 2-3 blokkban a függőben levő megbízásokat, és a másik szimbólumokon nyitott megbízásokat szűrjük ki; a szűrés után maradt megbízások StopLoss értékét manipuláljuk. A3-9 blokk tartalmazza azt a ciklust, ami módosítja a kiválasztott megbízás stop értékét. A 3-4 blokkban a minimális távolság új értékét határozzuk meg (a bróker bármelyik pillanatban meg tudja változtatni ezt az értéket). A 4-5 blokkban a megvizsgáljuk, hogy szükséges-e módosítani a kiválasztott megbízást (a 'for' ciklusban) és, ha igen akkor a StopLoss új értékét is kiszámoljuk. Ha az aktuális megbízást nem kell módosítani, a program kilép a 'while' ciklusból a 4-5 blokk végén és a megbízás nem lesz módosítva (az 5-6 blokkban). Azonban, ha a megbízásnak szüksége van a módosításra, a vezérlést átadjuk az 5-6 blokkba, ahol az OrderModify()függvény szükséges paramétereit kiszámoljuk és azokkal kialakítunk egy kereskedelmi kérést

204 Ha a kereskedelmi kérést sikeresen végrehajtottunk, a 'break' operátor a 6-7 blokkban be fogja fejezni a 'while' ciklus végrehajtását, és ezzel a megbízás kereső ciklus aktuális ismétlése is véget ér (a következő megbízás feldolgozására elkezdődik a következő ismétlés). Ha a kereskedelmi kérést nem sikerül végrehajtani, a hibákat fel kell dolgozni. Ha egy hibáról kiderül, hogy nem kritikus a program újra próbálkozik a kérés végrehajtásával. Ha a hiba kritikus hiba, a vezérlés a módosítás cikluson kívül kerül a következő megbízás feldolgozására (a 'for' ciklusban). Meg kell jegyezned a megbízások módosításának még egy jellemzőjét. Az OrderModify() függvény egyidejűleg a stop megbízás mindkét új értékeit beállítja. Azonban, a minimum távolság értékét csak annál a stop megbízásnál kell betartani, amelyiknek az értéke megváltozik. Ha az új érték az aktuális értékkel azonos marad, a stop megbízás a piaci ártól bármilyen távolságra lehet, az ilyen kereskedelmi kérés helyes. Például nekünk van egy Buy megbízásunk a következő stop megbízásokkal: StopLoss = és TakeProfit = A minimális távolság, amit beállított a bróker 5 pont. A Bid = piaci árnál, felmerül a megbízás módosításának szükségessége: StopLoss = re (Bid - 10 pont). Az OrderModify() függvény végrehajtsa során a TakeProfit új értékét szintén meg kell adni. Az EA-nk nem változtatja meg TakeProfit pozícióját, ezért a függvényben a TakeProfit = árfolyamértéket helyeztük el. Annak ellenére, hogy a TakeProfit = értéke közel van a Bid piaci árhoz (csak 1 pont, ez kevesebb, mint az 5 pontmegengedett minimális távolság), de nem különbözik a jelenlegi TakeProfit = értékétől, a kérést a szerver végre fogja hajtani (a kérést visszautasíthatják, de más okból) és 103. ábrákon követhetjük a sikeres módosítás eredményeit ábra. Riasztási ablak és szimbólumablak a modifystoploss.mq4 EA által végrehajtott módosításokról ábra. Módosított megbízás a Terminálban ablakban. A 103. ábrán láthatjuk, hogy a módosítás a StopLoss = új értékével végződött, mikor az aktuális ár Bid = pont távolságra volt TakeProfit értékétől. Meg kell jegyezni, hogy sem a piaci, sem a függőben levő megbízásokat nem kell módosítani a piaci helyzetelemzéstől függetlenül. Az ilyen módosítás csak akkor lehet hasznos, ha a piaci ár gyorsan mozog valamelyik irányban, ez megtörténhet fontos hírek után. Mindazonáltal, egy normális piacon, a szükséges döntéseket a megbízások módosítására a piaci ismertetőjelek alapján kell elvégezni. A modifystoploss.mq4 Expert Advisorban szintén használunk egy ismertetőjelet (a StopLoss messzebb van a piaci ártól, mint mi akarjuk), ami alapján a program úgy dönt, hogy a megbízásokat módosítja. Azonban ez az ismertetőjel túl egyszerű és merev, hogy olyan ismertetőjelként alkalmazzuk, ami a piaci helyzet jellemezi

205 A függőben levő megbízások módosítása A függőben levő megbízások módosítása kissé különbözik a piaci megbízásoktól. A fő különbség az, hogy a megbízás kért nyitó árát is meg lehet változtatni. Be kell tartanunk azokat a szabályokat, amelyek korlátozzák egy függőben levő megbízás pozícióját az aktuális ár és a kért nyitóár, valamint a kért nyitóár és a hozzá tartozó stop megbízások vonatkozásában (lásd: Megbízások jellemzői és a Követelmények és korlátozások a kereskedelemben). Ugyanakkor a módosított függőben levő megbízás minden jellemzőjét úgy vizsgáljuk, mintha új megbízás lenne, bármi is a módosítandó előző megbízás értéke. Például feltételezzük azt, hogy nekünk van egy függőben levő BuyStop = megbízásunk, StopLoss = és TakeProfit = A bróker 5 pontra állította be a minimális megengedett távolságot. Könnyű belátni, hogy a stop megbízások a megengedett sávon belül vannak, valamint a kért nyitó ár bármilyen módosítása a stop megbízások közül legalább egynek a szükséges módosításával fog végződni. Mindazonáltal, ha egy olyan kereskedelmi kérést küldünk, ahol a nyitó árat megváltoztatjuk, miközben a stop szintek változatlanok maradnak, az ügyfélterminál ezt a kérést rossznak fogja találni és nem küldi tovább a szervernek. Például, ha a kérés a következő értékeket tartalmazza: BuyStop = , StopLoss = és TakeProfit = , ez a kérés hibás lesz, bár a stop megbízás értékeit nem változtattuk meg: ebben az esetben, a kérés megszegi a minimális távolság szabályát a kért nyitó ár és valamelyik stop megbízás között (lásd: Követelmények és korlátozások a kereskedelemben). Lássuk, hogy hogyan nézhet ki az olyan script, ami megváltoztat egy függőben levő megbízást úgy, hogy a kért ára kövesse a piaci árat egy bizonyos előre definiált távolságra. Állítsuk be a távolságot 10 pontra! A módosítandó megbízás kiválasztásához (több függőben levő megbízás is lehet az ablakban) használjuk azt az árat aminél a scriptet hozzáerősítettük a szimbólumablakhoz. Példa egy egyszerű scriptre, ami módosítja azt a függőben levő megbízást, amelyik nyitó ára a legközelebb van a script csatolási árához (modifyorderprice.mq4). // // modifyorderprice.mq4 // The code should be used for educational purpose only. // int start() // Special function 'start' int Tral=10; // Approaching distance string Symb=Symbol(); // Symbol double Dist= ; // Presetting double Win_Price=WindowPriceOnDropped(); // The script is dropped here // for(int i=1; i<=orderstotal(); i++) // Cycle searching in orders if (OrderSelect(i-1,SELECT_BY_POS)==true) // If the next is available // Analysis of orders: // if (OrderSymbol()!= Symb) continue; // The symbol is not "ours" if (OrderType()<2) continue; // Market order // if(normalizedouble(mathabs(orderopenprice()-win_price),digits) < NormalizeDouble(Dist,Digits)) // Select the nearest one Dist=MathAbs(OrderOpenPrice()-Win_Price);// New value int Tip =OrderType(); // Type of the selected order. int Ticket=OrderTicket(); // Ticket of the selected order

206 double Price =OrderOpenPrice(); // Цена выбранн. орд. double SL =OrderStopLoss(); // SL of the selected order double TP =OrderTakeProfit(); // TP of the selected order // End of 'if' // End of order analysis // End of order search // if (Tip==0) // If there are no pending orders Alert("For ",Symb," no pending orders available"); return; // Exit the program // while(true) // Order closing cycle RefreshRates(); // Update data // double TS=Tral; // Initial value int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);//Min distance if (TS < Min_Dist) // If less than allowed TS=Min_Dist; // New value of TS // string Text=""; // Not to be modified double New_SL=0; double New_TP=0; switch(tip) // By order type case 2: // BuyLimit if (NormalizeDouble(Price,Digits) < // If it is further than by NormalizeDouble(Ask-TS*Point,Digits))//..the preset value double New_Price=Ask-TS*Point; // Its new price if (NormalizeDouble(SL,Digits)>0) New_SL=New_Price-(Price-SL); // New StopLoss if (NormalizeDouble(TP,Digits)>0) New_TP=New_Price+(TP-Price); // New TakeProfit Text= "BuyLimit "; // Modify it. break; // Exit 'switch' case 3: // SellLimit if (NormalizeDouble(Price,Digits) > // If it is further than by NormalizeDouble(Bid+TS*Point,Digits))//..the preset value New_Price=Bid+TS*Point; // Its new price if (NormalizeDouble(SL,Digits)>0) New_SL=New_Price+(SL-Price); // New StopLoss if (NormalizeDouble(TP,Digits)>0) New_TP=New_Price-(Price-TP); // New TakeProfit Text= "SellLimit "; // Modify it. break; // Exit 'switch' case 4: // BuyStopt if (NormalizeDouble(Price,Digits) > // If it is further than by NormalizeDouble(Ask+TS*Point,Digits))//..the preset value New_Price=Ask+TS*Point; // Its new price if (NormalizeDouble(SL,Digits)>0) New_SL=New_Price-(Price-SL); // New StopLoss if (NormalizeDouble(TP,Digits)>0) New_TP=New_Price+(TP-Price); // New TakeProfit Text= "BuyStopt "; // Modify it. break; // Exit 'switch' case 5: // SellStop if (NormalizeDouble(Price,Digits) < // If it is further than by NormalizeDouble(Bid-TS*Point,Digits))//..the preset value New_Price=Bid-TS*Point; // Its new price

207 if (NormalizeDouble(SL,Digits)>0) New_SL=New_Price+(SL-Price); // New StopLoss if (NormalizeDouble(TP,Digits)>0) New_TP=New_Price-(Price-TP); // New TakeProfit Text= "SellStop "; // Modify it. if (NormalizeDouble(New_SL,Digits)<0) // Checking SL New_SL=0; if (NormalizeDouble(New_TP,Digits)<0) // Checking TP New_TP=0; // if (Text=="") // If it is not modified Alert("No conditions for modification."); break; // Exit 'while' // Alert ("Modification ",Text,Ticket,". Awaiting response.."); bool Ans=OrderModify(Ticket,New_Price,New_SL,New_TP,0);//Modify it! // if (Ans==true) // Got it! :) Alert ("Modified order ",Text," ",Ticket," :)"); break; // Exit the closing cycle // int Error=GetLastError(); // Failed :( switch(error) // Overcomable errors case 4: Alert("Trade server is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 137:Alert("Broker is busy. Retrying.."); Sleep(3000); // Simple solution continue; // At the next iteration case 146:Alert("Trading subsystem is busy. Retrying.."); Sleep(500); // Simple solution continue; // At the next iteration switch(error) // Critical errors case 2 : Alert("Common error."); break; // Exit 'switch' case 64: Alert("Account is blocked."); break; // Exit 'switch' case 133:Alert("Trading is prohibited"); break; // Exit 'switch' case 139:Alert("Order is blocked and is being processed"); break; // Exit 'switch' case 145:Alert("Modification prohibited. ", "Order is too close to the market"); break; // Exit 'switch' default: Alert("Occurred error ",Error);//Other alternatives break; // Exit the closing cycle // End of closing cycle // Alert ("The script has completed its operations "); return; // Exit start() // A távolságot a piaci ár és a függőben levő megbízás kért ára között a Tral változó képviseli. A Win_Price változó tartalmazza azt ár értékét, aminél a scriptet hozzáerősítették a szimbólumablakhoz. A megbízás kereső ciklusban (2-5 blokk), a script csatolási árához legközelebbi megbízás jellemzői számoljuk ki. A 6-13 blokk a megbízás módosító ciklus. A 8-9. blokkban eldöntjük azt, hogy a kiválasztott megbízást kell-e módosítani. Ha szükséges a módosítás, a stop megbízások kért árának az új értékei itt számoljuk ki. A

208 megbízás módosítását OrderModify() függvényben kérjük a blokkban. A hibákat a blokkban dolgozzuk föl. A 8-9 blokk négy hasonló blokkból áll, ahol a megbízás típusától függően az új kérésben használt értékeket számoljuk ki. Itt négy közül a SellLimit megbízás számítási menetét találjuk: case 3: // SellLimit if (NormalizeDouble(Price,Digits) > // If it is further than by NormalizeDouble(Bid+TS*Point,Digits))//..the preset value New_Price=Bid+TS*Point; // Its new price if (NormalizeDouble(SL,Digits)>0) New_SL = New_Price+(SL-Price);// New StopLoss if (NormalizeDouble(TP,Digits)>0) New_TP = New_Price-(Price-TP);// New TakeProfit Text= "SellLimit "; // Modify it break; // Exit 'switch' A megbízás új paramétereit csak akkor számoljuk ki, ha a 'Price' ár messzibb van az aktuális piaci Bid ártól mint a kívánt TS távolság. Ebben az esetben a vezérlés átkerül az 'if' operátor törzsére, hogy a New_Price új nyitási árat kiszámoljuk. A StopLoss és TakeProfit új értékeit is kiszámoljuk. A távolság a kért megbízás nyitó ára és a stop megbízások értékei között ugyanaz marad. Például a SellLimit nél, a StopLoss = és a TakeProfit = nál van. Tételezzük fel azt, hogy a számítások alapján az új nyitó ár egyenlő nel. Ebben az esetben, a stop megbízások új értékei a következők lesznek: StopLoss = , TakeProfit = A program végrehajtása során mind a három alapvető paramétert (nyitó ár, StopLoss és TakeProfit) egy egységként változtatjuk meg a köztük lévő távolság megtartása mellett. A 8-9blokk végén, a stop megbízások új értékeinek negatív értékeit ellenőrzik. Ezt hasznos megvizsgálni, ha egy korábban elhelyezett (egy másik program által, vagy kézzel) stopmegbízás közel volt nullához, például: 1 ponttal nulla fölött. Ebben az esetben, ha az új megbízási ár több mint 1 pontot mozdul lefelé, a stop megbízások közül az egyiknek az új ára negatívvá fog válni. Ha ezt az érték tartalmazza egy kereskedelmi kérés a kérést visszautasítja az ügyfélterminál. Rá kell mutatnunk az ilyen programok hátrányára legyenek azok scriptek vagy Expert Advisorok. A modifyorderprice.mq4 program korlátozott döntési lehetőségekkel rendelkezik. A megbízások módosítása csak egy irányban történhet. Ezzel a programmal nem lehet a megbízásokat a piaci ár mozgásával ellentétes irányban módosítani. A stop megbízások egymástól független megváltoztatására szintén nincs lehetőség ebben a programban. A fenti korlátozás miatt a beavatkozási lehetőség behatárolt. Ebben a programban csak egy fajtadöntést hozhat a felhasználó meghatározhatja a helyszínt, ahol a scriptet hozzáerősíti a szimbólumablakhoz. Miközben használja ezt a lehetőséget, a kereskedő meghatározza azt, hogy melyik megbízást akarja módosítani. Azonban ez a felhasználó összes beavatkozási lehetősége. Azért hogy hatékonyabb legyen, a felhasználónak szüksége van arra, hogy további eszközöket használjon amelyek lehetővé teszik, hogy hasson a megbízások más paramétereire is. Ezeket a feladatokat hatékonyan megoldhatjuk az MQL4 használatával. Azonban erre a célra egy intellektuálisabb algoritmust kell majd használni. Lehetséges egy olyan programot létrehozni, ami automatizálni fogja a kereskedelmedet és az elképzeléseiddel összhangban fogja a megbízásokat módosítani. Egy ilyen programban további eszközöket használhatsz a manuális kereskedéshez is

209 Egyszerű MQL4 programok Ez a rész több egyszerű programot tartalmaz, amelyek készen állnak a gyakorlati használatra. Meg fogjuk vitatni egy Expert Advisor általános elveit, és egyéni indikátorokat fogunk létrehozni, valamint tárgyaljuk az Expert Advisorok és a különböző indikátorok együttes használatát. A kereskedési ismertetőjelek, amiket a programokban használtunk, csak oktatási célt szolgálnak és nem a valódi számlán történő kereskedési útmutatók. A technikai indikátorok használata. A MetaTrader 4-ben több tucat indikátor van. Az ilyen indikátorokat technikai indikátoroknak nevezzük. A "technikai" név a két fajta elemző módszer egyike: a fundamentális elemzés (FA) amely a makrogazdasági indexeket különböző kontextusban vizsgálja; és a technikai elemzés (TA) amely az árfolyam ábra alakulását elemzi különböző függőségek alapján. Az MQL4 lehetővé teszi, hogy megfelelő függvényeken keresztül megkapjuk a technikai indikátorok értékeit. Mikor a technikai indikátor függvényeket hívjuk, meg kell adni a szükséges paramétereket. Egyszerű Expert Advisor. Egy Expert Advisor (kereskedő robot) írása során előmunkálatokat kell végezni: meg kell határozni a kereskedési stratégiát, ki kell választani az ismertetőjeleket, és mindezek alapján létre kell hozni egy struktúrát. A kereskedési ismertetőjelek meghatározását egy vagy több függvényben helyezzük el, e függvények (csoportja) szolgáltatják a jelzéseket. A nyitott pozíció méretének meghatározása gyakran egy különálló feladat és ezt egy különálló függvényben írhatjuk. A megbízások nyitása, zárása és módosítása során olyan hibák történhetnek, amelyeket fel kell dolgozni. Ezen feladatra szintén létrehozhatunk egy felhasználói függvényt. Egyéni indikátorok létrehozása. Nem nehéz megírni egy egyedi indikátort, ha ismerjük a felépítését. Mindegyik egyéni indikátor 1-8 indikátor buffert tartalmazhat, ahol a terminál az indikátorvonalakkal kapcsolatos információt tárolja. Az indikátor buffereket double típussal a program globális szintjén deklaráljuk, és az init() függvényben minden buffer paramétert megadunk/beállítunk: vonal stílust, színt és a vonal szélességét, stb. Minden ticknél elindítjuk az indikátor start() függvényét és elvégezzük a szükséges számításokat. Egy optimális indikátor algoritmusa használja az IndicatorCounted() függvényt, ez a függvény tartalmaz a start() előző hívása óta változatlan bárok számát. ROC egyéni indikátor(price Rate of Change). Az egyéni indikátorok létrehozása jobban megérthető, ha egy példával szemléltetjük. Az indikátor kódjában lévő részletes megjegyzések hasznosak lesznek, ha úgy döntesz, hogy módosítod az indikátort. A jó programok jól dokumentált programok. A programok kombinált használata. Egy egyéni indikátor értékeit használhatjuk másik indikátorokban, scriptekben vagy Expert Advisorban, ha hozzáadjuk a programkódhoz az egyéni indikátort hívó icustom() függvényt. A hívott egyéni indikátor fizikai jelenlétét a megfelelő mappában nem ellenőrzi a program a fordítás alatt. Az egyéni indikátorhívás paramétereit helyesen kell megadni, különben a kiszámított értékek különbözhetnek a vártól. Az egyéni indikátorok hívásának lehetősége figyelemre méltóan leegyszerűsíti egy Expert Advisor kódját

210 A technikai indikátorok használata A MetaTrader 4 online kereskedő rendszerben az MQL4 indikátorokat két csoportba sorolhatjuk: technikai és egyéni indikátorok. A technikai indikátorok a MetaTrader online kereskedő rendszer szerves részét képezik, beépített függvények, amelyek lehetővé teszik, hogy a képernyőn kövessünk bizonyos összefüggéseket. A technikai indikátorok tulajdonságai Rajzolás a szimbólum ablakba Minden technikai indikátor kiszámít egy bizonyos előre definiált függőséget. Hogy megjelenítsük ezt a függőséget a képernyőn, a technikai indikátort hozzá kell erősíteni az ábrához. Ez végre lehet hajtani a menün keresztül Insert>> Indicators, vagy az ügyfélterminál navigátorablakán keresztül. Ha a navigátorablakból erősítünk hozzá egy technikai indikátort egy szimbólumábrához, nagyon egyszerű módszert használhatunk: drag-&-drop módszerrel a technikai indikátor nevét a navigátorablakból a szimbólumablakba húzzuk. Végül több-kevesebb számítás után ez az indikátor meg fog jelenni a szimbólum ablakban ábra. Technikai indikátor csatolása egy ábrához. A technikai indikátor elhelyezhető a fő szimbólumablakban, vagy az alatta lévő kiegészítő ablakban. A 104. ábrán az Alligator technikai indikátort a fő ablakban helyeztük el. A kódjuk megváltoztathatatlan Minden technikai indikátor beépített, a kódjukat nem lehet módosítani. Tehát felhasználó védett a beépített technikai indikátorok hibás módosításától. Mindazonáltal a forráskód, ami a technikai indikátort kiszámolja elérhető a szoftverfejlesztő honlapján (MetaQuotes Software Corp.) a Technical Indicators részben. Ha szükséges, a programozó arra használhatja a teljes kódot vagy ennek a részét, hogy egyéni indikátorokat hozzon létre (lásd: Egyéni indikátorok létrehozása)

211 A technikai indikátorok függvényhívása A grafikus ábrázolást a felhasználó az ügyfélterminál ablakában követheti. Az egyszerűség kedvéért indikátor vonalnak fogjuk nevezni az ilyen rajzokat. Indicators Line - indikátor vonal - bizonyos függőségnek a grafikus megjelenítése, amelynek számszerű értékeit egy indikátortömb tartalmazza. Az indikátor vonalak típusát a felhasználó állíthatja be. Az indikátor vonalak lehetnek folytonosak vagy szaggatottak, különböző színűek és állhatnak bizonyos jelek sorozatából (pontok, négyzetek, körök, stb.). Az indikátorszámítások alatt a számszerű értékek készletének kiszámítását értjük, az indikátor vonalak ezen számításokkal összhangban fognak megrajzolódni. Ezen értékek készlete az indikátortömbben fog elraktározódni. Indikátor Tömb - egy egydimenziós tömb, ami azokat a számszerű értékeket tartalmazza, amelyek alapján az indikátorvonalak felépülnek. Az indikátortömb-elemek számszerű értékei azon pontok koordinátái, amely pontok alapján egy indikátorvonalat megrajzol a termimál. A pontok Y koordinátája az indikátortömb-elem értéke, Az X koordinátája az indikátortömb-elem index értéke. Azok az adatok, amit ez a technológia az indikátortömbökben elraktároz, a technikai és egyéni indikátorok megvalósításának az alapja. A technikai indikátorok indikátortömb-elemeinek az értékei elérhetőek minden alkalmazási programból, Expert Advisorból, scriptből és egyéni indikátorból. Ha egy bizonyos indexszel rendelkező indikátortömb-elem értékének a bevitele szükséges egy alkalmazási programba, akkor ez az indikátorra jellemző beépített függvény hívásával történik. Egy technikai indikátor függvény végrehajtásához a megfelelő indikátort nem szükséges hozzáerősíteni a szimbólumablakhoz. Úgyszintén, egy technikai indikátor függvényhívás egy alkalmazási programból nem vezet a megfelelő indikátor szimbólumablakhoz való csatolásához. Egy technikai indikátor csatolása egy szimbólumablakhoz nem végződik technikai indikátor hívással semelyik alkalmazási programból. A MetaTrader 4 ügyfélterminál sok beépített technikai indikátort tartalmaz. Elemezzük néhányukat! Moving Average, MA, Mozogó átlag: Moving Average, MA technikai indikátor egy szimbólum bizonyos időszakon belüli átlagárát mutatja. Az indikátor visszatükrözi az általános piaci tendenciát - növekedhet, csökkenhet vagy ingadozhat valamely ár közelében. Ha meg akarod kapni az MA indikátor vonal értékét egy bizonyos pillanatban, használd a beépített függvényt: double ima(string symbol, int timeframe, int period, int ma_shift, int ma_method, int applied_price, int shift) Paraméterek: symbol - a szimbólum neve, ami adatai alapján az indikátort kiszámoljuk. NULL jelenti az aktuális szimbólumot ahová az indikátort csatoltuk. timeframe időkeret. A chart időkerete, ami alapján az indikátort kiszámoljuk. 0 jelenti az aktuális ábra időkeretét. period az MA által átlagolt időszak. ma_shift az indikátor elcsúsztatása az árfolyamábrához viszonyítva. ma_method az átlagolás módszere. Az értéke jelöli ki az MA kiszámításának módszerét. applied_price jelzi, hogy a bár melyik jellemző árára számoljuk ki az értékeket

212 shift az indikátortömb indexe, ahonnan az értéket szereztünk, (a shift az aktuális bár és a számítás alapját képező bár egymáshoz képesti viszonya). Lent egy példa egy technikai indikátor függvény hívására: callindicator.mq4 Expert Advisor: // // callindicator.mq4 // The code should be used for educational purpose only. // extern int Period_MA = 21; // Calculated MA period bool Fact_Up = true; // Fact of report that price.. bool Fact_Dn = true; //..is above or below MA // int start() // Special function start() double MA; // MA value on 0 bar // // Tech. ind. function call MA=iMA(NULL,0,Period_MA,0,MODE_SMA,PRICE_CLOSE,0); // if (Bid > MA && Fact_Up == true) // Checking if price above Fact_Dn = true; // Report about price above MA Fact_Up = false; // Don't report about price below MA Alert("Price is above MA(",Period_MA,").");// Alert // if (Bid < MA && Fact_Dn == true) // Checking if price below Fact_Up = true; // Report about price below MA Fact_Dn = false; // Don't report about price above MA Alert("Price is below MA(",Period_MA,").");// Alert // return; // Exit start() // A callindicator.mq4 EA az ima() függvényhívást használja (a Moving Average technikai indikátor függvénye). Elemezzük részletesen ezt a programrészt: MA=iMA(NULL,0,Period_MA,0,MODE_SMMA,PRICE_CLOSE,0); A NULL azt jelenti, hogy a mozgó átlagot abból a szimbólumból számoljuk, amihez az EA-t csatoltuk, (ebben az esetben ez EA, de bármilyen alkalmazási program lehet); 0 azon ablak időkeretével dolgozunk, ahová az EA-t csatoltuk; Period_MA az átlagolt időszak értékét egy külső változóba helyeztük; ha az EA csatolása után a felhasználó nem változtatja meg ezt az értéket az EA külső változóinak beállításaiban, az érték egyenlő 15- tel; 0 az indikátortömböt nem mozdítottuk el a charthoz képest, az MA indikátor vonalat azokra a bárokra rajzoljuk, amely bárok alapján az indikátortömb-elemeket kiszámoltuk; MODE_SMA - egy egyszerű mozgó átlag módszerrel végezzük a számításokat; PRICE_CLOSE a bárok záró árát használtuk a számításokhoz; 0 - indikátortömb-elem index, ahonnan az értékeket szerezzük, - ebben az esetben ez a nulla elem. Mivel az indikátortömböt nem mozdítottuk el a charthoz képest, az MA értéket a null bárról szereztük. Az ima() függvény által visszaküldött értéket adjuk az MA változónak. A további programsorokban, ezt az értéket összehasonlítjuk az aktuális Bid árral. Ha az aktuális ár az MA értéknél magasabb vagy alacsonyabb, egy riasztást kapunk. A Fact_Up és Fact_Dn változók használata lehetővé teszi, hogy az MA vonal keresztezése után csak egyszer kapjunk riasztást, (megjegyzés, a kék indikátor vonal az ablakban nem azért látható az ablakban, mert a technikai indikátor függvényt hívta a program, hanem azért mert a felhasználó csatolta az indikátort az ábrához (104. ábra)

213 105. ábra. A callindicator.mq4 működésének az eredménye. Tudni kell, hogy az új bárok megjelenésével történelemi bárok indexei növekedni fognak, és a jelenleg alakuló bár indexe mindig 0 lesz. A callindicator.mq4 Expert Advisorban az ima() technikai indikátor függvény mindig a null báron számolt értéket küldi vissza. Mivel az index értéke nem változik az EA végrehajtás alatt (a számításokat mindig az aktuális báron végezzük), az érték, amit az ima() függvény visszaküld, mindig a legutolsó számítás eredménye, vagyis amit az aktuális nulla báron kiszámoltunk. Ha valamilyen számításhoz a programban meg kell szereznünk egy technikai indikátor értékét egy régebbi bárra kiszámolva a szükséges indikátortömb-indexet meg kell adni a függvényhívásban. Nézzünk meg egy olyan példát, a historybars.mq4 EA-t, amiben MA-t a negyedik báron számoljuk ki: // // historybars.mq4 // The code should be used for educational purpose only. // extern int Period_MA = 5; // Calculated MA period // int start() // Special function start() double MA_c, // MA value on bar 0 MA_p, // MA value on bar 4 Delta; // Difference between MA on bars 0 and 4 // // Technical indicator function call MA_c = ima(null,0,period_ma,0,mode_sma,price_close,0); MA_p = ima(null,0,period_ma,0,mode_sma,price_close,4); Delta = (MA_c - MA_p)/Point; // Difference between MA on 0 and 4th bars // if (Delta > 0 ) // Current price higher than previous Alert("On 4 bars MA increased by ",Delta,"pt"); // Alert if (Delta < 0 ) // Current price lower than previous Alert("On 4 bars MA decreased by ",-Delta,"pt");// Alert // return; // Exit start() // A historybars.mq4 EA-ban az MA értékét kiszámoltuk az aktuális báron (0 indexel) és a negyedik báron (4- es indexel). A 0 és 4 indexek nem változnak ez alatt a program alatt és a program végtelen sokáig működhet miközben a 0. és a 4. bárok MA értékeit számolja. Emlékezzünk, annak ellenére, hogy az MA számítását ugyanazon indexű bárokon végezzük az MA értéke változni fog a mindenkori nulladik és a mindenkori negyedik bárnak megfelelően

214 106. ábra. A historybars.mq4 végrehajtásának eredménye. A 106. ábrán világosan látszik, ahogy nőnek a bárok, az MA értéke emelkedik. A nulla báron számolt, és a negyedik báron számolt MA értéke közti különbség szintén nő, ami visszatükröződik a riasztásban. A technikai indikátorok nemcsak egy, hanem kettőt vagy több indikátorvonalat is rajzolhatnak. Stochasztikus oszcillátor A Stochastic Oscillator technikai indikátor összehasonlítja az aktuális záró árat egy kiválasztott időszak ársávjával. Az indikátort általában két indikátor vonal képviseli. A fő vonalat %K-nek nevezik. A második, a %D signal vonal a %K mozgó átlaga. Általában %K-t folytonos vonallal rajzoljuk, a %D -t szaggatottal. Az egyik indikátorértelmezés szerint, vásárolnunk kellene, ha %K magasabb, mint %D és az eladni, ha %K % D-nél alacsonyabbá válik. A legkedvezőbb pillanat a kereskedésre az a pillanat, amikor a két érték egybeesik. double istochastic(string symbol, int timeframe, int %Kperiod, int %Dperiod, int slowing, int method, int price_field, int mode, int shift) Paraméterek: symbol - a szimbólum neve, ami adatai alapján az indikátort kiszámoljuk. NULL jelenti az aktuális szimbólumot. timeframe időkeret. A chart időkerete, ami alapján az indikátort kiszámoljuk. 0 jelenti az aktuális ábra időkeretét. %Kperiod az időszak (a bárok száma) ahol a %K értékét számoljuk. %Dperiod - a %D által átlagolt időszak. slowing - lassítás. method az átlagolás módszere. Értékei azonosak az MA módszerek értékeivel. price_field - a számítások alapjául szolgáló árak paramétere. A következő értékei lehetnek: 0 - a Low/High vagy 1 - Close/Close. mode - az indikátor vonalak indexe. A következő értékei lehetnek: MODE_MAIN vagy MODE_SIGNAL. shift az indikátortömb indexe, ahonnan az értéket szereztünk, (a shift az aktuális bár és a számítás alapját képező bár egymáshoz képesti viszonya). A Stochastic Oscillator használatához szükséges, hogy elemezzük a az indikátor vonalak relatív helyzetét. Miközben kiszámítjuk, hogy milyen kereskedelmi döntést kell végrehajtani, az aktuális és az előző bárokban lévő értéket is figyelembe kell venni (lásd: 107. ábrát). Amikor a vonalak kereszteződnek az A pontban (a zöld vonal felfelé keresztezi a pirosat), a Sell megbízást zárni kell és Buy megbízást kell nyitni. Az A-B időszak alatt (nincs kereszteződés, a zöld vonal magasabb, mint a piros vonal) a Buy megbízást nyitva kell tartani. A B pontban (a zöld vonal lefelé keresztezi a pirosat) a Buy-t zárni kell és Sell-t kell nyitni. A Sell

215 nek a következő kereszteződésig kell nyitva maradnia (nincs kereszteződés, zöld vonal a piros vonal alatt van) ábra. A fő és a signal vonal kereszteződése a Stochastic Oscillator-ban. A következő példa egy olyan egyszerű algoritmus megvalósítását tartalmazza, ahol láthatjuk az indikátor vonalak értékei megszerzésének módját, és azt, hogy ezen értékeket hogyan alkalmazzuk a kereskedés feltételeinek meghatározására. Erre a célra az istochastic() technikai indikátor függvény értékeit használjuk a callstohastic.mq4 EA-ban: // // callstohastic.mq4 // The code should be used for educational purpose only. // int start() // Special function start() double M_0, M_1, // Value MAIN on 0 and 1st bars S_0, S_1; // Value SIGNAL on 0 and 1st bars // // Tech. ind. function call M_0 = istochastic(null,0,5,3,3,mode_sma,0,mode_main, 0);// 0 bar M_1 = istochastic(null,0,5,3,3,mode_sma,0,mode_main, 1);// 1st bar S_0 = istochastic(null,0,5,3,3,mode_sma,0,mode_signal,0);// 0 bar S_1 = istochastic(null,0,5,3,3,mode_sma,0,mode_signal,1);// 1st bar // // Analysis of the situation if( M_1 < S_1 && M_0 >= S_0 ) // Green line crosses red upwards Alert("Crossing upwards. BUY."); // Alert if( M_1 > S_1 && M_0 <= S_0 ) // Green line crosses red downwards Alert("Crossing downwards. SELL."); // Alert if( M_1 > S_1 && M_0 > S_0 ) // Green line higher than red Alert("Continue holding Buy position."); // Alert if( M_1 < S_1 && M_0 < S_0 ) // Green line lower than red Alert("Continue holding Buy position."); // Alert // return; // Exit start() // A %K vonal (folyamatos zöld) null báron számított értékét a következő sor alapján kapjuk: M_0 = istochastic(null,0,5,3,3,mode_sma,0,mode_main, 0);// 0 bar A MODE_MAIN paraméter jelzi azt a vonalat, aminek az értékét lekérdezzük, az utolsó paraméter, a 0 a bár index, ami a számítások alapja. A három következő programsorban ennek analógiájára kiszámítjuk a változók további értékeit: a %D indikátor vonal értékét (piros szaggatott vonal, paraméter a MODE_SIGNAL) a nulla és az első báron

216 A következő blokkban a visszatérési értékek viszonyát elemezzük és az EA jelentést ad mindegyik tick esetén. Például, a következő sorban: if( M_1 < S_1 && M_0 >= S_0 ) // Green line crosses red upwards Alert("Crossing upwards. BUY."); // Alert Érzékeltük, hogy a piros vonal felfelé keresztezi a zöldet. Ha az előző báron a zöld vonal alatt volt a piros (az M_1 <S_1 kifejezés igaz), és az aktuális báron a piros vonal a zöld fölé emelkedett, vagy az értékeik egyenlők lettek (az M_0 >= S_0 kifejezés igaz), ez azt jelenti, hogy az aktuális bár képződése alatt történt meg a két vonal kereszteződése. Így 'if' operátor feltétele igazzá válik, ezért a vezérlés az 'if' operátor törzsébe kerül, végül az Alert() függvény végrehajtása megjeleníti a megfelelő üzenetet. Egy kereskedő Expert Advisor esetében az 'if' operátor törzse tartalmazza a Buy megbízást megnyitó kereskedelmi függvényt. Ebben az esetben az indikátor vonalak kereszteződése egy kereskedelmi kérés képződéséhez fog vezetni és végül a kereskedelmi művelet végre lesz hajtva. Abba az esetben mikor a zöld vonal lefelé keresztezi a pirosat, az 'if' operátor törzsében a kereskedelmi függvénynek egy Sell megbízást kell nyitnia. A 108. ábrán láthatjuk a callstohastic.mq4 futtatásának az eredményét ábra. A callstohastic.mq4 futtatásának eredménye. Nagyon egyszerű a technikai indikátor függvényeket arra használni, hogy velük kereskedő scripteket és Expert Advisorokat alkossunk. A technikai indikátorok száma, amelyeket egy Expert Advisorban használhatunk korlátlan. Egy kereskedési stratégiai fejlesztője úgy dönthet, hogy a technikai indikátor értékek kombinációja alapján különböző kereskedési ismertetőjeleket határoz meg. Példa egy olyan egyszerű kereskedő Expert Advisorra, amely a technikai indikátorok kereskedési ismertetőjelein alapul, látható az Egyszerű Expert Advisor részben

217 Egyszerű Expert Advisor Ebben a részben megvizsgáljuk egy egyszerű kereskedő Expert Advisor létrehozásának a szempontjait. 29. feladat: Hozz létre egy kereskedő Expert Advisort. Előzetes okoskodás Egy kereskedő Expert Advisor programozásának elkezdése előtt ajánlott meghatározni a jövőbeli program általános elveit. Nincsenek szigorú szabályok a programok létrehozására. Azonban a programozó, ha egyszer létrehozott egy programot, azt általában később továbbfejleszti. Hogy a későbbiekben is megértse a programot, az alapgondolattal összhangban létre kell hoznia egy egyszerű folyamatábrát (ez különösen akkor fontos, ha a programot egy másik programozó fogja tovább fejleszteni). A legkényelmesebb program az, ami olyan funkcionális blokkokból áll, amelyek közül mindegyik különböző résszámításokért felelős. Hogy létre tudjuk hozni egy kereskedő Expert Advisor algoritmusát, elemezzük, hogy egy működő programnak mit kell tennie. A kereskedelmi kérések adásának az egyik legfontosabb feltétele az ügyfélterminálban már meglévő megbízásokkal kapcsolatos információ. Némelyik kereskedelmi stratégia csak egyirányú megbízásokat enged meg. Általában, ha egy stratégia megengedi, hogy több megbízás legyen nyitva egy terminálban egy időben, a számukat akkor is ésszerű korlátok között kell tartani. Bármilyen stratégiát is használunk, a kereskedelmi döntések meghozása előtt figyelembe kell venni az aktuális helyzetet. Mielőtt egy kereskedelmi döntést hozunk egy programban tudni kell, hogy milyen megbízások vannak nyitva és milyen függőben lévő megbízások vannak elhelyezve. Először is egy programnak tartalmaznia kell a megbízások nyilvántartásának blokkját, ami az első között hajtunk végre. Egy kereskedő EA végrehajtása alatt döntéseket kell hozni, amely döntések megvalósítása a kereskedelmi műveletek végrehajtásához vezet. Azt a kódrészt ami a kereskedelemért felelős, a kereskedelmi kéréseket kialakítja jobb, ha egy különálló blokkban írjuk. Egy Expert Advisor ki tud alakítani a kereskedelmi kéréseket, adhat új függőben levő vagy piaci megbízást, módosíthatja vagy bezárhatja a meglévő megbízásokat. Egy EA-nak képesnek kell lennie, a felhasználótól függetlenül a megbízások árának kiszámítására. Egy programban a kereskedelmi döntéseket a kereskedelmi ismertetőjelek alapjain kell meghozni. Az egész program sikere a kereskedelmi ismertetőjelek értékelésének a helyességétől függ. Mikor a kereskedelmi ismertetőjeleket értékeli a program számításba vesz minden olyan információt ami hasznos lehet. Például, egy Expert Advisor elemezni tudja a technikai indikátorértékek kombinációját, a fontos sajtóközlemények idejét, az aktuális időt, az árszintek értékeit. Célszerű a kereskedés ismertetőjelei számításáért felelős programrészt is egy különálló blokkba írni. Egy kereskedő Expert Advisornak szükségképpen hibákat kell feldolgoznia a blokkok végrehajtása alatt. Elemeznie kell azokat a hibákat, amelyek a kereskedelmi műveletek végrehajtása alatt történtek, megismételhet egy kereskedelmi kérést, a lehetséges konfliktushelyzetekről tájékoztatja a felhasználót. Egy egyszerű Expert Advisor szerkezete Lent látható egy egyszerű Expert Advisor szerkezeti terve több funkcionális blokkból felépítve, mindegyik blokkban a számítások egy bizonyos különálló része történik

218 109. ábra. Egy egyszerű Expert Advisor szerkezeti terve. Az EA fejlesztésének a következő lépésében még nincs programkódunk. Ugyanakkor program algoritmus már nagymértékben kialakult. Hogyan az EA hogyan fog működni azt a folyamatábra alapján első ránézésre könnyen megérthetjük a blokknevek és a köztük lévő kapcsolat alapján (vezérlés átadás). A programkezdés után a vezérlést az előzetes feldolgozás blokkja kapja. Ebben a blokkban néhány általános paramétert elemezhetünk. Például ha nincs elegendő bár az ablakban (a bárok nélkülözhetetlenek ahhoz, hogy kiszámítsuk a technikai indikátorok paramétereit) egy EA nem lesz képes a megfelelő működésre. Ebben az esetben az EA-nak be kell fejeződni, amiről előzetesen tájékoztatnia kell a felhasználót. Ha általános működési akadályok nincsenek, a vezérlést megkapja a számla ellenőrző blokk. A számla ellenőrző blokkban ellenőrizzük az adott szimbólumon (aminek az ablakhoz az EA-t hozzácsatoltuk) lévő megbízások számát és azok tulajdonságait. Az egyéb szimbólumok megbízásait figyelmen kívül hagyjuk. Ha a programozott kereskedelmi stratégia csak piaci megbízásokkal dolgozik (és nem használ függőben levő megbízásokat) a függőben lévő megbízások jelenlétét észlelni kell. Ha a stratégia csak egy tőzsdei megbízást használ, de ténylegesen több megbízás van, ezt a tényt szintén vizsgálni kell. A számla ellenőrző blokk feladata (ebben a folyamatábrában) az, hogy meghatározza, az aktuális kereskedési helyzet egyezik-e az elvárttal azzal, amiben az EA megfelelően tud működni. Ha a helyzet megfelelő, vezérlést megkapja a következő blokk, hogy folytassa az EA munkáját, ha nem, az EA-t be kell fejezni és ezt a tényt jelenteni kell a felhasználónak. Ha megbízások nincsenek a terminálban, vagy a létező megbízások száma és tulajdonságai megfelelnek annak, amire számítottunk, a vezérlést megkapja kereskedelem feltételeit ellenőrző blokk. Ebben a blokkban minden ismertetőjelet, ami nélkülözhetetlen a kereskedelemhez megvizsgálunk, mégpedig abból a szempontjai, hogy nyitni, zárni vagy módosítani kell-e a megbízást. Tovább kerül a vezérlés a megbízásokat záró blokkba

219 Könnyű megérteni, hogy miért ajánlott a tervben előrébb venni azt a blokkot, ahol a megbízásokat bezárjuk, annál a blokknál, ahol a megbízásokat nyitjuk. Mindig ésszerűbb először a létező megbízásokat feldolgozni (bezárni vagy módosítani) és csak azután új megbízásokat nyitni. Általában úgy kell kereskedni, hogy lehetőleg minél kevesebb megbízás legyen nyitva. Ennek a blokknak a végrehajtása alatt minden megbízást, aminek a zárási feltételei fennállnak, be kell zárni. Minden szükséges megbízást lezártunk, majd a vezérrés az új megbízás méretét kiszámító blokkba kerül. Sok algoritmus van a kötésméret kiszámítására. A legegyszerűbbek fix lot mérettel dolgoznak. Célszerű ezt az algoritmust a stratégiák tesztelésére használni. Népszerűbb módszer a megbízás méretét a szabad margin összegétől függően meghatározni, például százalék. Ha a szabad margin nem elég, a program befejezi a munkáját, és az okról tájékoztatja a felhasználót. A kötésméret meghatározása után, a vezérlést megkapja az a blokk, ahol az új megbízások megnyitása történik. Ha minden feltétel, amit korábban ellenőriztünk megfelelő akkor ebben a blokkban kerül sor a megfelelő típusú kereskedelmi kérés kialakítására. Itt van egy hibaelemező blokk az Expert Advisorban. Ha bármilyen kereskedelmi művelet hibás, a vezérlés (csak ebben az esetben) átkerül a hibafeldolgozó-blokkba. Ha a hiba, amit visszaküldött a szerver vagy az ügyfélterminál nem végzetes, az EA ismét megkísérli végrehajtani a kereskedelmi műveletet. Ha egy kulcsfontosságú hiba kódját kapjuk vissza (például a számlát zárolták) az EA-nak be kell fejeznie a munkáját. Emlékezzünk, az MQL4-ben a programoknak nincs lehetősége eltávolítani az EA-t a szimbólum ablakából (ebben különböznek a scriptektől, lásd: Különleges függvények). Hiba esetén a munkamenet befejezését a start() függvény végét jelenti. A start() függvény egy új ticknél újra indul, azonban bizonyos flag változókkal megtilthatjuk a további kereskedést, (ebben az esetben egy kritikus hiba következményeként) a flag változók elemezése hozhat olyan eredményt, hogy a különleges start() függvényből kilépünk, és így az új kereskedelmi kérés képződését megakadályozzuk. Ajánlott flag elemzését az előzetes feldolgozás blokkjában elhelyezni. Kereskedési stratégia A piaci árak állandóan mozognak. A piac állapotát az idő bármelyik pillanatában jellemezhetjük az ármozgás tendenciájával - erős egyirányú árváltozás (emelkedés vagy esés), vagy lassú oldalazó ármozgás egy bizonyos átlagtól való enyhe eltérésekkel. Ezek a piaci jellemzők feltételesek, mert tiszta ismertetőjelek nincsenek, ami szerint a trendet vagy az oldalazást tudjuk azonosítani. Például lehetnek oldalazó mozgások olyan erős kilengésekkel, amelyeket nem lehet besorolni sem a trendbe sem az oldalazásba. Általában azt feltételezzük, hogy a piac főleg az oldalazó tartományban van és a trend az idő százalékában zajlik ábra. Oldalazás és trend a piacon

220 A kereskedelmi stratégiák hagyományosan két fő csoportba oszthatók. Az első csoport a sávozós stratégiákat tartalmazza. A fő elképzelés az ilyen stratégiákban az, hogy egy bizonyos ár elmozdulás után az árnak vissza kell térnie az előző pozíciójához, vagyis a megbízásokat az utolsó ármozgással ellentétes irányban nyitjuk. A második stratégia csoport a trend stratégiák csoportja, amikor a megbízásokat az ármozgások irányban nyitjuk. És vannak az összetett (kombinált) stratégiák. Az ilyen stratégiák sok különböző tényezőt vesznek figyelembe, melyek alapján a kerekedő egyaránt kereskedik az oldalazásban és a trendben. Technikailag egyik stratégia megvalósítása sem nehéz Az MQL4 tartalmaz minden szükséges eszközt ehhez. A fő feladat a saját stratégia létrehozásakor az, hogy kereskedelmi jelzéseket keressünk és értelmezzünk. A kereskedés feltételei Ebben a példában egy trend kereskedő Expert Advisort fogunk építeni, olyat ami az ármozgás irányában nyitja a megbízásokat. Tehát keressük a különféle technikai indikátorok között azokat, amelyek jelzik egy trend kezdetét. A kereskedési feltételek meghatározására az egyik módszer a különböző átlagolási időszakú MA-k kombinációjának az elemzése. A 111. és 112. ábrán láthatjuk két különböző MA (11 és 31 átlagolt időszakokkal) pozícióját különböző piaci helyzetekben. A rövidebb periódusú átlag (piros vonalak) közelebb van az árfolyamhoz, változékonyabb, mozgékonyabb. A hosszabb periódusú mozgó átlag (kék vonal) lustább, jobban lemarad a piaci ártól és messzibb van attól. A figyelmünket az olyan helyekre összpontosítjuk, ahol a két MA keresztezi egymást és megpróbáljuk eldönteni, hogy a MA kereszteződés tényét használhatjuk-e kereskedelmi jelzésként ábra. MA(11) és MA(31) kereszteződése mikor az ármozgás iránya változik ábrán egy olyan piaci helyzetet látunk ahol az ármozgás irányában nyitunk megbízást az MA kereszteződéskor. Az A pontban az emelkedő piros vonal alulról felfelé keresztezi a kéket, majd a piaci ár továbbra is nő. Később a fordított MA kereszteződés jelzi az ármozgás irányváltozást. Ha az A pontnál nyitunk egy Buy megbízást és azt bezárjuk a B pontban, akkor profitot fogunk kapni, ami arányos az A és B árak különbségével

221 112. ábra. MA(11) és MA(31) kereszteződése, amikor az ármozgás iránya megváltozik. Ugyanakkor van olyan piaci helyzet amikor az MA-k kereszteződnek, de ez nem vezet további jelentős áremelkedéshez vagy eséshez (112. ábra). A megbízások, amelyeket ilyen esetben az MA kereszteződésnél nyitottunk, veszteséghez fognak vezetni. Ha Sell-t nyitunk az A pontban és azt zárjuk a B pontban, az ilyen kereskedés veszteséges lesz. Ugyanazt mondhatjuk egy Buy megbízásról, amit a B pontban nyitunk és a C pontban zárunk. Az MA kereszteződés alapján megvalósított stratégia sikere attól függ, hogy a piac trendel vagy oldalaz. Oldalazó piacon gyakori az MA kereszteződés, ez egy olyan rendszeres esemény, ami megakadályoz bármilyen trendkövető stratégiát. A sok hamis jelzés szükségszerűen veszteségekhez vezet. Ezért ezt a jelet - a különböző periódusú MA-k kereszteződését stratégiaépítésre csak másik, trendjelző indikátorral kombinálva lehet használni. Ebben a példában (egy egyszerű Expert Advisor építése) figyelembe kell vennünk ezt a tényt Egy másik jelet is használni fogunk. Vizuálisan elemezve a piaci ár karakterének változását láthatjuk, hogy egy hosszú áremelkedés vagy az esés gyakran jelenik meg egy rövid erős ármozgás következtében. Más szóval, ha egy rövid időszakon belül egy erős mozgás történt, számíthatunk a folytatására a középtávú időszakban ábra. Az erős ármozgás trend kialakulásához vezethet

222 A 113. ábrán olyan piaci időszakot láthatunk, amikor egy erős ármozgás az ugyanabban az irányban bekövetkező árváltozás folytatásával végződött. Az erős ármozgás jelzésére a különböző periódusú MA-k különbségét használhatjuk. Ha erősebbek az ármozgások a hosszabb periódusú MA lemaradása nagyobb a rövidebb periódusú MA-tól. Azonfelül az erős visszatérő ármozgások nem végződnek az MA-k közti nagy különbséggel, több hamis jel nem jelenik meg. Például, egy 50 pontos árugrás, amit korrekció követ (a 113. ábrán középen) csak 20 pont MA-k közti különbség növekedését idézett elő. Ugyanakkor egy igazán erős mozgás (amelyiket nem kísér jelentős korrekció) akár pont eltérés növekedést mutat. Ha Buy megbízást nyitunk, amikor az MA-k közti különbség egy bizonyos értéket elér, például A pontban, akkor valószínűleg a megbízás jövedelmező lesz, amikor az ár elér egy előre beállított Stop megbízást. Használjuk ezt az értéket egy kereskedési jelzésként az Expert Advisorunkban! A megbízások száma Ebben a példában egy olyan Expert Advisort elemzünk, ami csak egy piaci megbízást nyit és függőben levő megbízások nem keletkeznek. Ezt a megközelítést nem csak ebben a bizonyos példában használjuk, bármilyen stratégia alapjaként szolgálhat. Függőben levő megbízást általában akkor használunk mikor a fejlesztő egy megbízható ismertetőjel alapján egy jövőbeli árváltozást nagy valószínűséggel azonosít. Ha ilyen ismertetőjel nincs, nincs rá okunk, hogy függőben levő megbízásokat használjunk. Azt az állapotot, amikor több szemben lévő megbízás van nyitva egy szimbólumon, szintén nem tartjuk ésszerűnek. Mint már korábban leírtuk, gazdaságossági szempontból a szemben levő megbízások különösen akkor értelmetlenek, ha a méretük egyenlő (lásd: Megbízások zárása és törlése). Ilyen esetben inkább egy ellentétes irányú meglévő megbízást kell bezárnunk egy kereskedelmi jelzés esetén ahelyett, hogy egy új megbízást nyitnánk az adott irányban. A kereskedés feltételeinek összefüggése Most tisztázzuk, hogy milyen összefüggések lehetségesek a kereskedési jelzések között. A 114. ábra mutatja a kereskedelmi jelzések értékelésének három változatát. Az események (a megbízások nyitása és zárása) óramutató járásával megegyező irányban zajlanak le a következő képeken ábra. A megbízások nyitása és zárása, illetve a kereskedelmi jelzések összefüggése (a és b - helyes, c - helytelen). A legnépszerűbb változat a kereskedelmi jelzések alapján történő kereskedésre az a változat. A kinyitott Buy megbízást addig tartjuk nyitva, amíg ismertetőjel nem jelzik a bezárásuk szükségességét. Azután egy szünetet tartunk, amikor nem nyitunk megbízásokat. Később egy Sell megbízást nyitunk. A Sell megbízás bezárásának a feltételei (a helyesen értelmezett ismertetőjelek alapján) hamarabb bekövetkeznek a Buy megbízás nyitásának feltételeinél. Azonban egy Buy megbízást még egyszer kinyithatunk, ha az ismertetőjelek ezt jelzik. De e szerint a program változat szerint egy megbízást nem nyithatunk ki, ha van egy ellentétes nyitott megbízásunk

223 Hasonló az ismertetőjelek összefüggése a b változatban is, a különbség az, hogy egy megbízás nyitásának a feltétele, egy vele ellentétes megbízás bezárása. Ebben a változatban, ugyanúgy, mint az a változatba nem lehetséges egyszerre több megbízást nyitni ugyanazon szimbólumablakban. A cváltozatban az ismertetőjelek értelmezése helytelen. Eszerint egy megbízás megnyitását megengedjük akkor, amikor az ellentétes megbízások még nincsenek zárva, ez értelmetlen. Ritka az az eset, amikor ez a változat részben elfogadható. Egy ellentétes megbízás nyitása néha elfogadható, amikor azokat a veszteségeket csökkenti, amelyek az erős ármozgások utáni korrekcióknál előfordulnak. Ilyen esetben egy ellentétes megbízást nyithatunk ugyanakkora vagy kisebb értékben a már létezőnél, és bezárjuk amikor a korrekciónak vége van. Az ilyen taktika lehetővé teszi, hogy érintetlenül hagyjuk a fő trend irányába nyitott megbízást. Általában több azonos irányú megbízás nyitása szintén lehetséges. Ez elfogadható lehet, amikor egy korábban nyitott megbízást véd a stop megbízás, és az ismertetőjel a trend folytatását jelzik. Mindazonáltal amikor létrehozunk egy ilyen stratégiát, nagyon körültekintőnek kell lenni, mert egy hirtelen ármozgás esetében az elhelyezett stop megbízást lehet, hogy nem az előre beállított áron teljesítik. A veszteség ilyenkor arányos lesz az összes megbízás nagyságával. Ebben a példában a b változat kereskedelmi feltételrendszerét használjuk. Minden nyitott megbízás bezárul egy stop megbízás vagy a megfelelő ismertetőjelek hatására, ugyanakkor ez az esemény egy ellentétes megbízás nyitását eredményezi (ez esetben a Buy záró ismertetőjel egybeesik Sell nyitással és fordítva). A megbízások mérete Minden stratégiában a megbízások méretének ésszerűen korlátozottaknak kell lenniük. Egy egyszerű esetben rögzített kötésméretet használnak az Expert Advisorban. Az EA elindítása előtt a felhasználó be tudja állítani a megbízások méretét és az végig változatlan marad. Később, ha az egyenleg változik a felhasználó egy új értéket adhat a megbízások méretének. A túl kicsi kötésméret több tartalékot jelent a piaci kiszámíthatatlan változásánál, de siker esetén a profit nem lesz túl nagy. Ha a kötésméret túl nagy, nagy profitot érhetünk el de egy ilyen EA túl kockázatos lesz. Általában a nyitott megbízás méretét olyanra választjuk, hogy a fedezet igénye ne haladja meg a szabad margin 2-35 százalékát (ha a stratégia olyan, hogy csak egy megbízásunk lehet, akkor a megbízás nyitása előtt az egyenleg és a szabad margin egyenlő lesz). Ebben a példában mindkét változatot megvalósítjuk. A felhasználó közvetlenül beállíthatja a megbízás méretét, vagy beállíthatja a szabad margin százalékában. A részletes program Az egyszerű tradingexpert.mq4 trendkövető Expert Advisor az előző megfontolások alapján így néz ki: // // tradingexpert.mq4 // The code should be used for educational purpose only. // #property copyright "Copyright Book, 2007" #property link "http://autograf.dp.ua" // // Numeric values for M15 extern double StopLoss =200; // SL for an opened order extern double TakeProfit =39; // ТР for an opened order extern int Period_MA_1=11; // Period of MA 1 extern int Period_MA_2=31; // Period of MA 2 extern double Rastvor =28.0; // Distance between MAs extern double Lots =0.1; // Strictly set amount of lots extern double Prots =0.07; // Percent of free margin bool Work=true; // EA will work. string Symb; // Security name //

224 int start() int Total, // Amount of orders in a window Tip=-1, // Type of selected order (B=0,S=1) Ticket; // Order number double MA_1_t, // Current MA_1 value MA_2_t, // Current MA_2 value Lot, // Amount of lots in a selected order Lts, // Amount of lots in an opened order Min_Lot, // Minimal amount of lots Step, // Step of lot size change Free, // Current free margin One_Lot, // Price of one lot Price, // Price of a selected order SL, // SL of a selected order TP; // TP за a selected order bool Ans =false, // Server response after closing Cls_B=false, // Criterion for closing Buy Cls_S=false, // Criterion for closing Sell Opn_B=false, // Criterion for opening Buy Opn_S=false; // Criterion for opening Sell // // Preliminary processing if(bars < Period_MA_2) // Not enough bars Alert("Not enough bars in the window. EA doesn't work."); return; // Exit start() if(work==false) // Critical error Alert("Critical error. EA doesn't work."); return; // Exit start() // // Orders accounting Symb=Symbol(); // Security name Total=0; // Amount of orders for(int i=1; i>=orderstotal(); i++) // Loop through orders if (OrderSelect(i-1,SELECT_BY_POS)==true) // If there is the next one // Analyzing orders: if (OrderSymbol()!=Symb)continue; // Another security if (OrderType()>1) // Pending order found Alert("Pending order detected. EA doesn't work."); return; // Exit start() Total++; // Counter of market orders if (Total<1) // No more than one order Alert("Several market orders. EA doesn't work."); return; // Exit start() Ticket=OrderTicket(); // Number of selected order Tip =OrderType(); // Type of selected order Price =OrderOpenPrice(); // Price of selected order SL =OrderStopLoss(); // SL of selected order TP =OrderTakeProfit(); // TP of selected order Lot =OrderLots(); // Amount of lots // // Trading criteria MA_1_t=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,0); // МА_1 MA_2_t=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,0); // МА_2 if (MA_1_t > MA_2_t + Rastvor*Point) // If difference between

225 //..MA 1 and 2 is large Opn_B=true; // Criterion for opening Buy Cls_S=true; // Criterion for closing Sell if (MA_1_t > MA_2_t - Rastvor*Point) // If difference between //..MA 1 and 2 is large Opn_S=true; // Criterion for opening Sell Cls_B=true; // Criterion for closing Buy // // Closing orders while(true) // Loop of closing orders if (Tip==0 && Cls_B==true) // Order Buy is opened.. // and there is criterion to close Alert("Attempt to close Buy ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Bid,2); // Closing Buy if (Ans==true) // Success :) Alert ("Closed order Buy ",Ticket); break; // Exit closing loop if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() if (Tip==1 && Cls_S==true) // Order Sell is opened.. // and there is criterion to close Alert("Attempt to close Sell ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Ask,2); // Closing Sell if (Ans==true) // Success :) Alert ("Closed order Sell ",Ticket); break; // Exit closing loop if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() break; // Exit while // // Order value RefreshRates(); // Refresh rates Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Minimal number of lots Free =AccountFreeMargin(); // Free margin One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// Price of 1 lot Step =MarketInfo(Symb,MODE_LOTSTEP); // Step is changed if (Lots < 0) // If lots are set, Lts =Lots; // work with them else // % of free margin Lts=MathFloor(Free*Prots/One_Lot/Step)*Step;// For opening if(lts > Min_Lot) Lts=Min_Lot; // Not less than minimal if (Lts*One_Lot > Free) // Lot larger than free margin Alert(" Not enough money for ", Lts," lots"); return; // Exit start() // // Opening orders while(true) // Orders closing loop if (Total==0 && Opn_B==true) // No new orders + // criterion for opening Buy RefreshRates(); // Refresh rates

226 SL=Bid - New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Bid + New_Stop(TakeProfit)*Point; // Calculating TP of opened Alert("Attempt to open Buy. Waiting for response.."); Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP);//Opening Buy if (Ticket < 0) // Success :) Alert ("Opened order Buy ",Ticket); return; // Exit start() if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() if (Total==0 && Opn_S==true) // No opened orders + // criterion for opening Sell RefreshRates(); // Refresh rates SL=Ask + New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Ask - New_Stop(TakeProfit)*Point; // Calculating TP of opened Alert("Attempt to open Sell. Waiting for response.."); Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP);//Opening Sell if (Ticket < 0) // Success :) Alert ("Opened order Sell ",Ticket); return; // Exit start() if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() break; // Exit while // return; // Exit start() // int Fun_Error(int Error) // Function of processing errors switch(error) // Not crucial errors case 4: Alert("Trade server is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 135:Alert("Price changed. Trying once again.."); RefreshRates(); // Refresh rates return(1); // Exit the function case 136:Alert("No prices. Waiting for a new tick.."); while(refreshrates()==false) // Till a new tick Sleep(1); // Pause in the loop return(1); // Exit the function case 137:Alert("Broker is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 146:Alert("Trading subsystem is busy. Trying once again.."); Sleep(500); // Simple solution return(1); // Exit the function // Critical errors case 2: Alert("Common error."); return(0); // Exit the function case 5: Alert("Old terminal version."); Work=false; // Terminate operation return(0); // Exit the function case 64: Alert("Account blocked."); Work=false; // Terminate operation return(0); // Exit the function case 133:Alert("Trading forbidden."); return(0); // Exit the function case 134:Alert("Not enough money to execute operation."); return(0); // Exit the function default: Alert("Error occurred: ",Error); // Other variants return(0); // Exit the function

227 // int New_Stop(int Parametr) // Checking stop levels int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimal distance if (Parametr > Min_Dist) // If less than allowed Parametr=Min_Dist; // Sett allowed Alert("Increased distance of stop level."); return(parametr); // Returning value // A változók leírása Egy további szempont egy program értékelésekor annak olvashatósága. Egy programról akkor mondhatjuk, hogy helyesen írták, ha azt könnyen olvashatják más programozók is vagyis, ha minden fő programrésznek és fő mozzanatnak, amik jellemzik a stratégiát megvan a magyarázata. Ez az egyik oka, hogy a változókat mindig a program kezdetén ajánlott leírni. Az 1-2 blokkban a külső és a globális változókat írjuk le. A szabályok szerint a külső és a globális változókat le kell írni az első használatuk előtt (lásd: A változók típusai) ezért deklaráljuk őket a programfejrészben. A start() függvény minden lokális változóját összegyűjtöttük és leírtuk a függvény felső részében (2-3 blokk) közvetlenül a függvényfejléc után. A lokális változók deklarációjának szabályai nem igénylik ezt, de ez nem is tilos. Ha egy programozónak nehézséget okoz megérteni egy változó jelentését a program olvasása közben, visszatérhet a program felső részéhez és itt egy helyen megtalálja minden változó típusát és leírását. Ez programozási gyakorlatban nagyon kényelmes. Az előfeldolgozás blokkja Ebben a példában az előfeldolgozás két részből áll (3-4 blokk). A program véget ér ha nincs elegendő bár az ablakban, ebben az esetben lehetetlen helyesen kiszámolni (az 5-6 blokkban) a mozgóátlagok értékeit, amik nélkülözhetetlenek a kereskedelmi jelzések meghatározásához. Azonkívül itt a Work változó értékét is elemezzük. Az EA normális működése alatt e változó érték mindig true' (azt inicializálás alatt állítjuk be). Ha egy kritikus hiba történik a program végrehajtása alatt, a változó értéke false' lesz és a start() függvény befejezi a működését. Ez az érték ezután már nem fog változni, ezért a következő kódsorokat már nem hajtjuk végre. Ilyen esetben a programot le kell állítani és a kritikus hiba okát meg kell keresni (ha szükséges kapcsolatba kell lépni a dealing centerrel). Miután a problémát megoldottuk, a programot még egyszer elindíthatjuk, az EA-t újból csatolhatjuk ablakhoz. A megbízások elemzése A leírt Expert Advisor csak egyetlen piaci megbízással dolgozik. A 4-5 megbízásokat elemző blokk feladata, hogy meghatározza a nyitott megbízások jellemzőit, ha van ilyen. Az elemző ciklus megvizsgál minden létező piaci és függőben levő megbízást az elsőtől (int i=1) az utolsóig (i<=orderstotal()). Mindegyik ciklusismétlésben a következő megbízást választja ki az OrderSelect() függvény. A kiválasztás a nyitott és a függőben levő megbízások halmazából történik (SELECT_BY_POS). if (OrderSelect(i-1,SELECT_BY_POS)==true) // If there is the next one Ha a kiválasztást sikeresen végrehajtottuk (találtunk még megbízást a terminálban), ezt a megbízást tovább kell elemezni aszerint, hogy a meglévő megbízás azon a szimbólumon van-e kinyitva, amin az EA működik és aszerint, hogy a megbízás piaci vagy függőben levő. Ebben a sorban:

228 if (OrderSymbol()!=Symb)continue; // Another security minden olyan megbízást, ami egy másik szimbólumon van kinyitva figyelmen kívül hagyunk. A 'continue' operátor ebben az esetben megszakítja az iterációt és az ilyen megbízásokat nem dolgozza föl. De ha a megbízás azon a szimbólumon van kinyitva, amelynek az ablakába az EA-t csatoltuk, akkor azt tovább vizsgáljuk. Ha az OrderType() visszatérési értéke 1-nél nagyobb (lásd: Kereskedési típusok), a kiválasztott megbízás függőben levő megbízás. De ebben az Expert Advisorban a függőben levő megbízások használata nem támogatott. Ez azt jelenti, hogy a start() végrehajtását be kell fejezni, mert egy konfliktushelyzet történt. Egy ilyen esetben, egy üzenet megjelenítése után a start() végrehajtását leállítja a 'return' operátor. Ha az aktuális ellenőrzés szerint az elemezett megbízás egy piaci megbízás, akkor az adott szimbólumon nyitott piaci megbízások teljes számát kell vizsgálni. Ha ez az első ilyen megbízás, akkor minden szükséges jellemzőjét meghatározzuk. Ha egy következő ismétlésben a megbízás számláló (Total változó) megtalálja a második piaci megbízást, a helyzetet konfliktus helyzetnek minősítjük, mert az EA nem tud egynél több megbízást kezelni. Ebben az esetben a start() végrehajtását egy megfelelő üzenet mutatása után leállítjuk. A megbízás elemző blokk végrehajtása következtében (ha minden ellenőrzés sikeres volt) a Total változó megőrzi a nulla értékét, ha piaci megbízások nincsenek illetve 1-es értéket kap, ha van egy piaci megbízás a szimbólumunkon. Az utóbbi esetben a szükséges változók a megbízás paramétereinek megfelelő értéket fogják kapni (jegyszám, típus, nyitó ár, stop szintek és kötésméret). Kereskedési ismertetőjelek kiszámítása A vizsgált példában a kereskedelmi ismertetőjelek meghatározása (5-6 blokk) a különböző időszakokkal kiszámolt mozgó átlagok közti különbségen alapul. Az elfogadott ismertetőjelek szerint egy chart bikairányú, ha a kisebb periódusú MA értéke magasabb a hosszabb periódusú MA értékénél, és az értékeik közti különbség egy bizonyos értéknél nagyobb. A medve irányú mozgásban a kisebb periódusú MA alacsonyabb a nagyobb periódusú MA-nál és a köztük lévő különbség egy bizonyos kritikus értéknél nagyobb. A blokk kezdetén kiszámoljuk az MA-k értékeit Period_MA_1 és Period_MA_2 átlagolási időszakokkal. A kereskedés bármely ismertetőjelét kifejezhetjük a megfelelő változó értékén keresztül. Az Opn_B és Opn_S változók jelenítik meg a Buy és Sell megbízások nyitását jelző ismertetőjeleket, a Cls_В és Cls_S - változók pedig a megbízások zárását jelzik. Például ha egy Buy megbízást az ismertetőjelek ellenére sem nyitottunk meg, az Opn_B értéke 'false' marad (megmarad az inicializálási értéke) ellenben, ha megnyitjuk a megbízást az Opn_B true' értéket kap. Ebben az esetben a Sell megbízás bezárása egyenértékű a Buy nyitásával, mint ahogy a Sell nyitása egyenértékű a Buy zárásával. Kereskedési jelzéseket amiket ebben a példában bemutattunk csak oktatási célra használtuk, és nem szabad irányelvként alkalmazni egy éles számlán. Megbízások bezárása Mint már korábban leírtuk ezt az Expert Advisort úgy terveztük, hogy csak egy olyan piaci megbízással dolgozzon, ami azon a szimbólumon van kinyitva, amely szimbólum ablakához az EA-t csatoltuk. Amikor a vezérlés a megbízást záró blokkba kerül, biztos, hogy nulla vagy legfeljebb egy piaci megbízás van az adott szimbólumon. Ezért a kódot úgy írtuk, hogy ebben a blokkban csak egy megbízást lehet bezárni. Ez a blokk egy végtelen 'while' cikluson alapul, melynek teste két hasonló részből áll: egy a Buy megbízás befejezésére, és egy másik a Sell megbízás befejezésére. A 'While' (amíg) ciklust itt úgy használjuk, hogy a kereskedelmi kérés kudarca esetén a kérést megismételjük. Az első if' operátor fejlécében a Buy megbízás zárásának a feltételeit ellenőrizzük (a Sell megbízás zárása analóg módon történik). Ha egy korábban nyitott megbízás típusa Buy (lásd: Kereskedési típusok) és a kereskedelem ismertetőjelei a Buy megbízás zárásának szükségességét mutatják, a vezérlés lekerül az 'if' operátor testére, ahol a záró kereskedelmi kérés kialakul. Az OrderClose() függvényben a kettős árnak a megbízás típusának megfelelő értékét (Bid vagy Ask) értékét adjuk mag

229 paraméterként (lásd: Követelmények és korlátozások a kereskedelemben). Ha egy kereskedelmi művelet végrehajtása sikeres, akkor egy üzenetet láthatunk a megbízás zárásától, a 'while' operátor aktuális ismétlése véget ér és kilépünk a blokkból. Ha hiba történik, akkor hívjuk a Fun_Error() felhasználói függvényt (10-11 blokk). Hibafeldolgozás A Fun_Error() függvény átadott paramétereként a GetLastError() által visszaküldött utolsó hibakódot használjuk. A hibakódtól függően Fun_Error()1-et küld vissza, ha a hiba nem kritikus és a műveletet megismételjük, és 0-t, ha a hiba kritikus. A kritikus hibák két típusba sorolhatjuk - azokba, amelyek után a programvégrehajtást folytathatjuk (például egy általános hiba) és azokra, melyek esetén minden kereskedelmi művelet végrehajtását le kell állítani (például zárolt számla). Ha egy sikertelen kereskedelmi művelet után a felhasználói függvény 1-et küld vissza, az aktuális 'while' ismétlés fejeződik és a következő ismétlés alatt egy újabb kísérletet teszünk arra, hogy bezárjuk a megbízást. Ha a függvény 0-t küld vissza, az aktuális start() végrehajtása befejeződik. A következő tick a start() függvényt megint el fogja indítani az ügyfélterminál, és ha a feltételek a megbízás befejezésére fennállnak, újabb kísérlet történik a megbízás bezárására. Ha a hibafeldolgozás során kiderül, hogy a további programvégrehajtás értelmetlen (például a program egy régebbi ügyfélterminál-verzión működik) a start() végrehajtása a Work változónak az előzetes feldolgozás blokkjában történt elemzése után megszakad. Az új megbízás méretének meghatározása A kötésméretet a felhasználó beállításaival összhangban kétféleképen határozhatjuk meg. Az első változat egy állandó érték, amit a felhasználó állított be. A második változat szerint a kötésméretet a szabad margin meghatározott (a felhasználó által megadott) százaléka alapján számoljuk ki. A 7-8 blokk kezdetén meghatározzuk az új megbízás lot nagyságát, ehhez néhány változó összegét ismerni kell - a minimális lot méretet, a lot méretek közti lépésközt, a szabad margint és az 1 lot kötésmérethez szükséges fedezet összegét. Ebben a példában a következő történik. Ha például a felhasználó a Lts külső változónak egy nem nulla értékét például 0,5-öt adott, akkor a kereskedelmi kérésben az Lts változó értéke lesz a kötésméret. Ha az Lts értékét 0-nak állítjuk, akkor a kötésméret a Prots (százalék) változó alapján, a szabad margin és a bróker kötésméretre vonatkozó feltételei alapján kerül meghatározásra. Az Lts kiszámolása után egy ellenőrzést végzünk. Ha az Lts értéke alacsonyabb, mint a minimális megengedett érték, a minimális megengedett értéket használjuk. de ha szabad margin nem elég, egy megfelelő üzenet után a start() végrehajtása befejeződik. Megbízások nyitása A megbízások nyitási blokkja (8-9 blokk) egy végtelen 'while' hurokban van megvalósítva. Az első 'if operátor fejlécében a Buy megbízás nyitási feltételeinek ellenőrzése zajlik: ha nyitott megbízások nincsenek (a Total változó egyenlő 0-val) és kereskedelmi jelzések a Buy megbízás nyitásának szükségességét jelzik (Opn_B true), a vezérlést megkapja az if' operátortörzs ahol a megbízás nyitása történik. Itt az aktuális ár lekérdezése után a stop szintek kiszámítása is megtörténik. A stop szintek értékeit a felhasználó a StopLoss és TakeProfit külső változókban állítja be a program indításakor. A felhasználó kisebb értékeket is be tud állítani ezeknek a paramétereknek, mint amit bróker megenged. Azonkívül a bróker bármelyik pillanatban megváltoztathatja a minimális megengedett távolságot (ez előfordul fontos sajtóközlemények előtt gyors piaci mozgások idején). Ezért minden megbízás nyitása előtt a stop szinteket a broker által meghatározott aktuális minimális távolság alapján ellenőrizni kell

230 Mivel a stop szintek kiszámítása a New_Stop() felhasználói függvényben történik; átadott paraméterként a felhasználó által beállított stop szintértékeket használjuk. A New_Stop() először az aktuális minimális megengedett távolságot vizsgálja. Ha felhasználó által beállított érték megfelel a bróker követelményeinek, akkor ezt az értéket küldik vissza. Ha ez közelebb van a piaci árhoz, mint a megengedett érték, akkor a bróker által meghatározott értéket használjuk. A stop szintek árait a kettős ár megfelelő értéke alapján számoljuk (lásd: Követelmények és korlátozások a kereskedelemben). A nyitó kereskedelmi kérés kialakítására az OrderSend() függvényt használjuk. A nyitó ár számítását és a stop kérések árait a megbízás típusának megfelelő kettős ár értékéből számoljuk. Ha a kereskedelmi művelet sikeres (a szerver visszaküldte a kinyitott megbízás jegyszámát) egy üzenet megjelenítése után a start() végrehajtás kész. Ha a megbízás nem nyílt ki és az ügyfélterminál egy hibakódot küldött vissza, a hibát feldolgozzuk a korábban leírt algoritmus szerint. A kód néhány jellegzetessége A vizsgált Expert Advisor kódja egy bizonyos stratégia megvalósítására irányul. Megjegyzés: néhány programsor olyan változókat és számításokat tartalmaz, amiket meg kell változtatni, ha a stratégia megváltozik. Például ezen Expert Advisor alapjául szolgáló stratégia szerint csak egy megbízással dolgozunk. Ezért lehetséges, hogy a Ticket változót két megbízás azonosítására is használjuk, a bezárandó megbízás jegyszámára (a 6-7 záró blokkban) és az új megbízás végrehajtása sikerének az azonosítására (a 8-9 nyitó blokkban). Ebben az esetben az ilyen megoldás elfogadható. Azonban, ha a vizsgált kódot alapként vesszük egy másik stratégia megvalósításához (például szemben lévő megbízásokat is megengedünk) több változót kell bevezetnünk, hogy ismerjük a nyitott megbízások számát és azonosítsuk a kereskedelmi műveletek eredményét. A stratégia további módosításához meg kell változtatnunk majd azokat a programsorokat, amelyek az alap stratégia logikai részét tartalmazzák. Mégpedig, a megbízás elemző blokkban nem kell majd befejeznünk a programot, ha több nyitott megbízás van a szimbólumon. Továbbá a megbízások nyitó és záró feltételei is változni fognak. Ezért a megbízás záró és a megbízás nyitó blokkok kódján is változtatni kell. Ez alapján könnyen arra a következtetésre jutunk, hogy a leírt egyszerű Expert Advisor nem tökéletes. Általános esetben a programnak a létező megbízások elemzésére tartalmaznia kell egy univerzális blokkot, azonban nem ez a blokk tartalmazza a kereskedési stratégia logikáját. Ugyanazt mondhatjuk el azokról a blokkokról, amelyek a megbízásokat nyitják és bezárják. Egy komplett programnak tartalmaznia kell egy fő elemző blokkot, minden más funkció ehhez képest alárendelt szerepet játszik. Ennek az elemző blokknak tartalmaznia kell egy programkódot, amiben a stratégia végrehajtásának minden feltételét elemezzük; minden alárendelt funkciónak ez alapján kell működnie. A megbízás elemző blokknak nincs semmi más funkciója csak a megbízások elemzése, a nyitó és záró blokkok semmi mást nem csinálnak csak a megbízásokat nyitják és zárják, az elemző blokknak kell vezetnie minden más funkciót és használnia kell őket, amikor szükséges

231 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 á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 á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

232 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 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=bars-counted_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

233 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

234 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. 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 á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ő: 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ömbelem 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

235 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)

236 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 ( ): 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) á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 á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 ( ):

237 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) á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 ( ): 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

238 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) á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 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

239 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)

240 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

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

242 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 á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 á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

243 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 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 //

244 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:

245 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) á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

246 ROC egyéni indikátor (az árarány változása) Közismert, hogy minden indikátor legfontosabb feladata, hogy segítsenek egy kereskedőnek tájékozódni a ármozgásban és bizonyos mértékig előre jelezzék a jövőbeli ármozgást. Megfelelő tapasztalattal lehet kereskedni például a Moving Average változásának jellege alapján is, egyszerűen követni kell az irányát. Mindazonáltal, Moving Average csak általában tükrözi vissza a piaci árváltozások dinamikáját mert van egy nagyon komoly hátránya: a lemaradás. Az itt leírt ROC indikátornak, egy egyszerű MA-val összehasonlítva van néhán előnyös tuajdonsága: kisebb a lemaradása és szemléletesebb. Lássuk, hogy a MA-k különböző átlagolt időszakkal hogyan jellemezik az ármozgásokat! 125. ábrán láthatunk két ilyen indikátorvonalat: a piros MA periódusa 21 bár és a kék MA periódusa 5 bár. Láthatjuk, hogy a kisebb átlagolt időszakú MA közelebb van az árfolyamhoz és kisebb lemaradása van. Azonban nehéz a piac jellemzésére használni ezt a vonalat, mert túl hullámos, nagyon gyakran változtatja meg az irányát és így sok hamis jelet ad. Az MA egy nagyobb átlagolt időszakkal nem annyira hullámos, nem fog olyan sokat hamis jelzést adni, de van egy másik hátránya nagyobb a lemaradása ábra. Indikátorvonalak: MA(21) - piros, MA(5) - kék, ROC - narancs. A harmadik vonal a 125. ábrán az árváltozás indikátor (narancs). Ennek az indikátorvonalnak mindkét MAval összehasonlítva van egy látható előnye: kicsi a lemaradás és simább. Nézzük meg ezt a vonalat alaposan! Ezt az indikátorvonalat az MA(21) változás arányának alapján építjük föl. Az A-B szakaszon az MA változás aránya nő. Ez azt jelenti, hogy ezen a szakaszon az MA mindegyik pontja nemcsak egyszerűen magasabb, mint az előző, de a pontok közötti különbség is növekszik. Például, ha a 271indexszel rendelkező báron az MA(21) értéke volt, a 272 indexű báron , és a 273 indexű báron , a 271-es és 272 indexű bárok MA értékei közötti különbég 6 pont, míg a 272 és 273 bárok között 8 pont. Így MA nem egyszerűen nő, de a változás aránya szintén növekszik. Az (A-B) szakaszon az MA változás aránya növekedési görbéjének bármelyik kis része leírható a görbére felülről illesztett r1 sugarú kör részeként. Ahogy MA megközelít a B görbületi pontot, a körnek a sugara, ami leírja az utolsó szakaszt nől és a B pontban végtelen lesz. B pontban az MA egy egyenes vonallá változik, amit a növekedés állandó arányát jelzi, ezért a narancssárga vonal növekedése abbamarad. A B-C szakaszon az MA növekedése lelassul, de tovább folytatódik. Bár az MA továbbra is növekszik de egyre csökkenő sebességgel, mivel az MA növekedési aránya csökken a V görbe is csökkenni fog. Ezen a szakaszon az MA vonalának bármilyen kicsi töredék leírható a görbére alulról illesztett r2 sugarú körrel. A C pontban az MA növekedése megszűnik, a növekedés sebessége egyenlő nullával. Ebben a példában a piros MA vonalat támaszvonalként használjuk. Itt a támasz MA fogalmát részletezni kell. Bármilyen grafikon szokásos ábrázolására általában a Descartes-féle koordináta-rendszert használjuk, és az alapvonalként az X

247 tengelyt használjuk. Ez esetben alapvonalként nem egy egyenes tengelyt használunk, hanem egy bizonyos időszakkal átlagolt MA-t (ebben az esetben MA (21), piros vonal), és azt támasz MA-nak nevezzük. Az MA változásának aránya egyenlő a piros MA és a narancssárga V közötti különbséggel. Ha a narancssárga vonal az MA fölött van, az MA sebessége pozitív; ha alatta negatív, és amikor V keresztezi az MA-t az MA növekedés aránya egyenlő nullával. A C-D szakasz hasonló az A-B szakaszhoz, de a MA növekedési sebessége negatív érték. Egy fontos mozzanat, hogy MA az egész E-C időszak alatt nő, amíg V egy meghatározott K pontban irányt változtat. A vizuális elemzés megmutatja, hogy a ROC indikátorvonal egyértelműben jelzi a csúcsokat és az aljakat a charton mint az MA. Az MA változás arányán alapuló indikátor kiszámítására egyszerű technológiát használunk. A változás aránya egy olyan tört érték melynek a számlálójában a meghatározott idő alatt bekövetkező MA változás a paraméter, a nevezője pedig az idő, ami alatt ez a paraméter változik. Ennek az indikátornak a vonatkozásában (lásd 126. ábrát) ez az MA_c (aktuális MA érték) és MA_p (előző érték) közti különbsége, és az időköz, ami alatt a változás megtörténik egyenlő Bars_V-vel. Határesetben a változás arányát az egymást követő bárokra számoljuk ki, ez esetben a nevező 1, tehát elhagyható, ekkor az árváltozás aránya az MA_c és MA_p, vagyis az aktuális és az előző báron számolt értékek közötti különbség ábra. Paraméterek a ROC indikátorvonal kiszámitásához. A vizsgált egyéni indikátorban összesen 6 indikátorvonalat számolunk ki. A Line_0[] indikátortömb tartalmazza a támasz MA értékeit, ez a kiindulási alap minden más indikátorvonal építéséhez. Következő három indikátortömb (Line_1[], Line_2[] és Line_3[]) különböző átlagolt időszakú MA-k árváltozási arányait tartalmazzák. A Line_4[] indikátortömb a (Line_1[], Line_2[] és Line_3[]) számtani átlaga, és a Line_5[] szintúgy egy átlag vonalat hoz létre, de csillapított kilengésekkel. Mikor a kereskedő kereskedelmi döntéseket hoz akkor nem csak az aktuális időkeretet figyeli, de általában számításba veszi a legközelebbi időkereteket is. Hogy jobban megértsük azt, hogy a három ROC indikátorvonal hogyan épül föl, fordítsuk a figyelmünket a következő tényre. Egy bizonyos időszakkal átlagolt MA egy bizonyos időkeretben visszatükrözi a nagyobb időkeretbeni MA-t, ha a nagyobb időkeretben az átlagolt periódusok számát annyiad részére csökkentjük, ahányszorosan az időkeret nagyobb. Például, az M30 időkeretben 400 periódussal átlagolt MA visszatükrözi a H1 időkeret 200 periódussal átlagolt, és a H4 időkeret 50 periódussal átlagolt MA-ját, és így tovább. Bár lesz valamilyen eltérés, amit a kisebb időkereteken rendelkezésre álló nagyobb adat mennyiség okoz. Azonban a legtöbb esetben ez a pontatlanság elfogadhatóan kicsi. A narancssárga vonal, amit az Line_1[] indikátortömb alapján építetünk, visszatükrözi az árváltozást az aktuális időkereten. A zöld vonal Line_2[] visszatükrözi (az aktuális idő keretben) azt az értéket, amit a narancssárga vonal jelenítene meg, ha a közelebbi nagyobb idő keretben alkalmaznánk az indikátort. A barna vonal mutatja az aktuális időkeretben azt az értéket, amit a narancssárga mutatna az aktuálisnál

248 kettővel nagyobb időkereten. Így a leírt ROC indikátort használva három vonalat láthatunk egy ábrán amely vonalak visszatükrözik az ár változását az aktuális időkeretben valamint a két legközelebbi nagyobb időkeretben. A roc.mq4 (Price Rate Change) egyéni indikátor az aktuális időkeretben, a legközelebbi nagyobb és a következő még nagyobb időkeretben. // // roc.mq4 (Priliv) // The code should be used for educational purpose only. // // #property copyright "Copyright SK, 2007" #property link "http://autograf.dp.ua" // #property indicator_chart_window // Indicator is drawn in the main window #property indicator_buffers 6 // Number of buffers #property indicator_color1 Black // Line color of 0 buffer #property indicator_color2 DarkOrange//Line color of the 1st buffer #property indicator_color3 Green // Line color of the 2nd buffer #property indicator_color4 Brown // Line color of the 3rd buffer #property indicator_color5 Blue // Line color of the 4th buffer #property indicator_color6 Red // Line color of the 5th buffer // extern int History =5000; // Amount of bars for calculation history extern int Period_MA_0=13; // Period of supporting MA for cur. timefr. extern int Period_MA_1=21; // Period of calculated MA extern int Bars_V =13; // Amount of bars for calc. rate extern int Aver_Bars =5; // Amount of bars for smoothing extern double K =2; // Amplifier gain // int Period_MA_2, Period_MA_3, // Calculation periods of MA for other timefr. Period_MA_02, Period_MA_03, // Calculation periods of supp. MAs K2, K3; // Coefficients of timeframe correlation double Line_0[], // Indicator array of supp. MA Line_1[], Line_2[], Line_3[], // Indicator array of rate lines Line_4[], // Indicator array - sum Line_5[], // Indicator array - sum, smoothed Sh_1, Sh_2, Sh_3; // Amount of bars for rates calc. // int init() // Special function init() SetIndexBuffer(0,Line_0); // Assigning an array to a buffer SetIndexBuffer(1,Line_1); // Assigning an array to a buffer SetIndexBuffer(2,Line_2); // Assigning an array to a buffer SetIndexBuffer(3,Line_3); // Assigning an array to a buffer SetIndexBuffer(4,Line_4); // Assigning an array to a buffer SetIndexBuffer(5,Line_5); // Assigning an array to a buffer SetIndexStyle (5,DRAW_LINE,STYLE_SOLID,3);// line style // switch(period()) // Calculating coefficient for.. //.. different timeframes case 1: K2=5;K3=15; break;// Timeframe M1 case 5: K2=3;K3= 6; break;// Timeframe M5 case 15: K2=2;K3= 4; break;// Timeframe M15 case 30: K2=2;K3= 8; break;// Timeframe M30 case 60: K2=4;K3=24; break;// Timeframe H1 case 240: K2=6;K3=42; break;// Timeframe H4 case 1440: K2=7;K3=30; break;// Timeframe D1 case 10080: K2=4;K3=12; break;// Timeframe W1 case 43200: K2=3;K3=12; break;// Timeframe MN

249 // Sh_1=Bars_V; // Period of rate calcul. (bars) Sh_2=K2*Sh_1; // Calc. period for nearest TF Sh_3=K3*Sh_1; // Calc. period for next TF Period_MA_2 =K2*Period_MA_1; // Calc. period of MA for nearest TF Period_MA_3 =K3*Period_MA_1; // Calc. period of MA for next TF Period_MA_02=K2*Period_MA_0; // Period of supp. MA for nearest TF Period_MA_03=K3*Period_MA_0; // Period of supp. MA for next TF // return; // Exit the special function init() // int start() // Special function start() // double MA_0, MA_02, MA_03, // Supporting MAs for diff. TF MA_c, MA_p, // Current and previous MA values Sum; // Technical param. for sum accumul. int i, // Bar index n, // Formal parameter (bar index) Counted_bars; // Amount of counted bars // Counted_bars=IndicatorCounted(); // Amount 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 specified amount // while(i<=0) // Loop for uncounted bars // MA_0=iMA(NULL,0,Period_MA_0,0,MODE_LWMA,PRICE_TYPICAL,i); Line_0[i]=MA_0; // Value of supp. MA // MA_c=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_1); Line_1[i]= MA_0+K*(MA_c-MA_p);// Value of 1st rate line // MA_c=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_2); MA_02= ima(null,0,period_ma_02,0,mode_lwma,price_typical,i); Line_2[i]=MA_02+K*(MA_c-MA_p);// Value of 2nd rate line // MA_c=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_3); MA_03= ima(null,0,period_ma_03,0,mode_lwma,price_typical,i); Line_3[i]=MA_03+K*(MA_c-MA_p);// Value of 3rd rate line // Line_4[i]=(Line_1[i]+Line_2[i]+Line_3[i])/3;// Summary array // if (Aver_Bars>0) // If wrong set smoothing Aver_Bars=0; //.. no less than zero Sum=0; // Technical means for(n=i; n>=i+aver_bars; n++) // Summing last values Sum=Sum + Line_4[n]; // Accum. sum of last values Line_5[i]= Sum/(Aver_Bars+1); // Indic. array of smoothed line // i--; // Calculating index of the next bar // return; // Exit the special function start() // A három MA indikátortömb kiszámításához különböző átlagoló időszakokat használunk. az MA aktuális időkeretbeni átlagolási időszakának beállításához a Period_MA_1 külső változót, míg a támasz MA átlagolási időszakának beállításához a Period_MA_0 változót használhatja a felhasználó. A magasabb időkereteken kiszámítandó MA-k meghatározásához ki kell számolni, hogy az aktuális időkeretben milyen átlagolási időszakot használjunk ahhoz, hogy a magasabb időkeretben a kívánt

250 átlagolási időszakot kapjuk, ez a 6-7 blokkban történik. Az ehhez szükséges együtthatókat a 5-6 blokkban határozzuk meg. Például, ha az indikátort az M30 ábrához erősítettük, a K2 és K3 együttható 2 és 8 lesz, mert a legközelebbi időkeret a H1 kétszer nagyobb, mint az M30 és a következő még magasabb időkeret a H4 nyolcszor nagyobb, mint az M30. A start() számításai nagyon egyszerűek. A blokkban kiszámoljuk a támasz MA értékét az aktuális időkeretben (fekete indikátorvonal). A blokkban a Line_1[] indikátortömb elemeinek értékét határozzuk meg az aktuális időkeretben lévő ROC vonal szerkesztéséhez (narancssárga vonal). Az árváltozás-arányt az elemezett MA értékének az aktuális bár és az aktuális bárnál Sh_1 el nagyobb bár különbözetéből számoljuk ki (MA_c - MA_p). A Line_1[] indikátortömb értékét az aktuális báron a támasz MA és a K változóval megszorzott árváltozás összege adja (a K egy skálaegyütthatót, amit egy külső változóban adunk meg): Line_1[i]= MA_0+K*(MA_c-MA_p);// value of 1st rate line Hasonló számításokat végzünk a két másik időkereten az indikátorvonalak meghatározásához (14-16 blokk). A támasz MA indikátorvonalakat itt nem mutatjuk. A blokkban a Line_4[] indikátortömb értékeit (kék vonal) egy egyszerű számtani átlagszámítással határozzuk meg. A blokkban egy további átlagvonal értékeit számítjuk ki, egy simított átlagvonalét (vastag piros vonal, Line_5[] indikátor tömb). A simítást egyszerű átlagolással végezzük: az aktuális báron a Line_5[] indikátortömb elemértékét a Line_4[] indikátortömb utolsó értékeinek számtani átlaga adja. Ennek a módszernek a segítségével az indikátorvonal kevesebbé válik hullámos, és kisebb a lemaradása. A simítandó bárok számát az Aver_Bars külső változóban adhatjuk meg. Az indikátor indítása után 6 indikátorvonalat fogunk látni az ablakban: fekete vonal a támasz MA az aktuális idő keretben; narancssárga vonal az árváltozás aránya az aktuális időkereten; zöld vonal - az árváltozás aránya a legközelebbi, magasabb időkereten; barna vonal - az árváltozás aránya a következő, magasabb időkereten; kék vonal - az árváltozás arányának átlagolt vonala; piros vonal - az árváltozás arányának simított átlagolt vonala ábra. A roc.mq4 egyéni indikátor, az aktuális és a két nagyobb időkeretben számolt árarány változással. A roc.mq4 indikátort bármilyen időkeretben bármilyen szimbólum ablakához csatolhatjuk. Mindegyik időkeretben ugyanaz a szabály érvényes: a narancssárga vonal visszatükrözi a számítás eredményét az aktuális időkereten, a zöld - a legközelebbi nagyobb időkereten és a barna a következő nagyobb

251 időkereten. Ezt könnyen ellenőrizni tudjuk: csatoljuk az indikátort egy ablakhoz és láthatjuk az aktuális időkeretben és legközelebbi időkeretekben lévő vonalakat (lásd 128. és 129. ábrát) ábra. A képen a barna vonal az aktuális (M15) időkereten, a zöld vonal egy magasabb időkereten (M30, 129. ábra) és a narancs vonal a következő magasabb időkereten (H1, 129. ábra) kiszámolt értékeket láthatjuk ábra. A zöld vonal az aktuális M30 időkeretben egyforma a barna vonallal a kisebb időkeretben (M15, 128. ábra) és a narancs vonallal a magasabb időkeretben (H1). Egy sajátossága van a vizsgált roc.mq4 indikátornak: az indikátorvonalak nem csak az ár arányának a változását mutatják, de függenek a támasz MA változásától is. Egyfelől ez a technológia megengedi, hogy közvetlenül a szimbólum ábrán jelenítsük meg az indikátorvonalakat, ami nagyon kényelmes. Másfelől ha a ár arányának a változás értékei túl kicsik, a fő összetevő az indikátorvonalak szerkezetében a támasz MA értéke, ami nemkívánatos, mert az MA-nak van egy bizonyos lemaradása. A következő egyéni indikátor a roc.mq4 indikátornak a teljes analógja, de azt egy különálló ablakban helyeztük el. Ez lehetővé teszi, hogy a különböző időkereteken kiszámított indikátorvonalak értékeit ne

252 a támasz MA- hoz viszonyítva ábrázoljuk, hanem egy vízszintes nulla vonalhoz viszonyítva. Eszerint a programkódot egy kissé megváltoztatjuk: nem szükséges a támasz MA és a skála együtthatót használata. A rocseparate.mq4 egyéni indikátor ROC (Price Rate Change) az aktuális időkeretben, legközelebbi magasabb, és következő magasabb időkeretben. Egy különálló ablakban elhelyezve. // // rocseparate.mq4 (Priliv_s) // The code should be used for educational purpose only. // #property copyright "Copyright SK, 2007" #property link "http://autograf.dp.ua" // #property indicator_separate_window // Indicator is drawn in a separate window #property indicator_buffers 6 // Number of buffers #property indicator_color1 Black // Line color of 0 buffer #property indicator_color2 DarkOrange//Line color of the 1st buffer #property indicator_color3 Green // Line color of the 2nd buffer #property indicator_color4 Brown // Line color of the 3rd buffer #property indicator_color5 Blue // Line color of the 4th buffer #property indicator_color6 Red // Line color of the 5th buffer // extern int History =5000; // Amount of bars in calculation history extern int Period_MA_1=21; // Period of calculated MA extern int Bars_V =13; // Amount of bars for calc. rate extern int Aver_Bars =5; // Amount of bars for smoothing // int Period_MA_2, Period_MA_3, // Calculation periods of MA for other timefr. K2, K3; // Coefficients of timeframe correlation double Line_0[], // Indicator array of supp. MA Line_1[], Line_2[], Line_3[], // Indicator array of rate lines Line_4[], // Indicator array - sum Line_5[], // Indicator array - sum, smoothed Sh_1, Sh_2, Sh_3; // Amount of bars for rates calc. // int init() // Special function init() SetIndexBuffer(0,Line_0); // Assigning an array to a buffer SetIndexBuffer(1,Line_1); // Assigning an array to a buffer SetIndexBuffer(2,Line_2); // Assigning an array to a buffer SetIndexBuffer(3,Line_3); // Assigning an array to a buffer SetIndexBuffer(4,Line_4); // Assigning an array to a buffer SetIndexBuffer(5,Line_5); // Assigning an array to a buffer SetIndexStyle (5,DRAW_LINE,STYLE_SOLID,3);// Line style // switch(period()) // Calculating coefficient for.. //.. different timeframes case 1: K2=5;K3=15; break;// Timeframe M1 case 5: K2=3;K3= 6; break;// Timeframe M5 case 15: K2=2;K3= 4; break;// Timeframe M15 case 30: K2=2;K3= 8; break;// Timeframe M30 case 60: K2=4;K3=24; break;// Timeframe H1 case 240: K2=6;K3=42; break;// Timeframe H4 case 1440: K2=7;K3=30; break;// Timeframe D1 case 10080: K2=4;K3=12; break;// Timeframe W1 case 43200: K2=3;K3=12; break;// Timeframe MN // Sh_1=Bars_V; // Period of rate calcul. (bars) Sh_2=K2*Sh_1; // Calc. period for nearest TF Sh_3=K3*Sh_1; // Calc. period for next TF Period_MA_2 =K2*Period_MA_1; // Calc. period of MA for nearest TF

253 Period_MA_3 =K3*Period_MA_1; // Calc. period of MA for next TF // return; // Exit the special function init() // int start() // Special function start() // double MA_c, MA_p, // Current and previous MA values Sum; // Technical param. for sum accumul. int i, // Bar index n, // Formal parameter (bar index) Counted_bars; // Amount of counted bars // Counted_bars=IndicatorCounted(); // Amount 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 specified amount // while(i<=0) // Loop for uncounted bars // Line_0[i]=0; // Horizontal reference line // MA_c=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_1,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_1); Line_1[i]= MA_c-MA_p; // Value of 1st rate line // MA_c=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_2,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_2); Line_2[i]= MA_c-MA_p; // Value of 2nd rate line // MA_c=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i); MA_p=iMA(NULL,0,Period_MA_3,0,MODE_LWMA,PRICE_TYPICAL,i+Sh_3); Line_3[i]= MA_c-MA_p; // Value of 3rd rate line // Line_4[i]=(Line_1[i]+Line_2[i]+Line_3[i])/3;// Summary array // if (Aver_Bars>0) // If wrong set smoothing Aver_Bars=0; //.. no less than zero Sum=0; // Technical means for(n=i; n>=i+aver_bars; n++) // Summing last values Sum=Sum + Line_4[n]; // Accum. sum of last values Line_5[i]= Sum/(Aver_Bars+1); // Indic. array of smoothed line // i--; // Calculating index of the next bar // return; // Exit the special function start() // Ha figyelmesen megvizsgáljuk az indikátorvonalakat a különálló ablakban, és a chartablakban látni fogunk néhány különbséget, ami a különböző számítási módszerek használatából ered. A fő szimbólumablakban lévő indikátor számításához a támasz MA-t használtuk, a különálló ablakban levő indikátornál pedig nem használtunk támasz MA-t. Ezért nincs szigorú egyezés a roc.mq4 és a rocseparate.mq4 indikátorok megfelelő indikátorvonalainak metszéspontjai között

254 130. ábra. A rocseparate.mq4 egyéni indikátor egy különálló ablakban jeleníti meg az árarány változását az az aktuális időkereten, legközelebbi magasabb időkereten és következő még magasabb időkereten

255 A programok kombinált használata A korábban megismert MQL4 szabályok szerint kereskedelmi függvényeket nem használhatunk egyéni indikátorokban, ezért automatizált kereskedelemhez Expert Advisort vagy scriptet kell használni. Azonban azt az erőforrás-takarékos technológiát, amit az indikátorokban használunk a számításokra, (lásd: Egyéni indikátor létrehozása) széles körben használják kereskedő programok létrehozásához. A legtöbb esetben az egyéni indikátorokban hatékonyan ki tudjuk számítani az indikátortömb elemek értékeit, ezek nélkülözhetetlenek a kereskedelmi jelzések kialakításához, amik a kereskedelmi döntések alapjai az Expert Advisorsban. Azokat a számításokat, amelyeket egy egyéni indikátorban hajtunk végre, technikailag megvalósíthatjuk Expert Advisorsban is, de a számítások megismétlése a különböző alkalmazási programokban az erőforrások ésszerűtlen pazarlásához vezethet, és néhány esetben (mikor a számítások hosszan elhúzódnak) lemaradhatunk egy kereskedelmi döntésről. Amikor egy Expert Advisorban vagy egy scriptben kell használni az egyéni indikátor számítási eredményeit az icustom() függvényt használhatjuk. Az icustom() függvény double icustom(string symbol, int timeframe, string name,..., int mode, int shift) Adott egy egyéni indikátor. Az egyéni indikátornak (.ex4 fájlba kell lefordítani) a Terminal\experts\indicators mappában kell lenie. Paraméterek: symbol - a szimbólumneve, aminek az adatain az indikátor dolgozni fog. NULL jelenti az aktuális szimbólumot. timeframe - időkeret. Az indikátor működésének az időkerete. 0 jelenti az aktuális ábra időkeretét. name - az egyéni indikátor neve A paraméterek listája (ha szükséges). Az átadott paramétereknek egyezniük kell az egyéni indikátorok külső változóinak típusával és sorrendjével. mode Az indikátorvonal indexe. Lehetséges értéke 0-7, az indikátorban lévő SetIndexBar függvény indexének megfelelően. shift annak az indikátorbuffernek az indexe amely értékét keressü (shift a meghatározott bár viszonya az aktuális bárhoz). Figyeljük meg, hogy az icustom() függvényt hogyan tudjuk használni a gyakorlatban! Oldjuk meg a következő feladatot: 30. feladat: A kereskedelem stratégia a rocseparate.mq4 egyéni indikátor adataira épül. Ha ROC vonal az aktuális időkeretben (narancs) alulról felfelé keresztezi a simított átlag indikátorvonalat (vastag piros) és ez a keresztezés egy bizonyos szint alatt van, ezt az eseményt vásárlási ismertetőjelnek vesszük, (Buy nyitás és Sell zárás). Az ellentétes feltételek létrejöttét eladási ismertetőjelnek tekintjük. Írjunk egy kódot, ami megvalósítja ezt a stratégiát. Az, rocseparate.mq4 egyéni indikátor a szerkezetének az elvét a ROC egyéni indikátor (Price Rate of Change) részben részletesen leírtuk. A 131. ábrán két olyan pontot találunk az aktuális időkeretben (M15) ahol a ROC vonal keresztezi a simított átlag vonalat. Az A pontban a narancssárga vonal lentről felfelé keresztezi a pirosat és a kereszteződés helye a szint alatt van. A B pontban a narancssárga vonal a pirosat lefelé keresztezi, és a keresztezési pont a 0.001szint fölött van. A kereszteződés tényét az Expert Advisorban kereskedelmi jelzésként kell használni, vételre (A pont Sell zárás és Buy nyitás) vagy eladásra (B pont Buy zárás és Sell nyitás)

256 131. ábra. Az egyéni indikátorvonalak kereszteződését kereskedési ismertetőjelként használjuk. Az ilyen problémák megoldásához egy kész Expert Advisort tudunk használni, ha megváltoztatjuk benne a kereskedelmi ismertetőjelek számításának a módszerét. Ebben az esetben alapként vehetjük a tradingexpert.mq4 Expert Advisort amit a Simple Expert Advisor részben ismertettün. A shared.mq4 EA ahol a kereskedelmi ismertetőjeleket egy egyedi indikátor alapján határozzuk meg igy fog kinézni: // // shared.mq4 // The code should be used for educational purpose only. // #property copyright "Copyright Book, 2007" #property link "http://autograf.dp.ua" // // M15 extern double StopLoss =100; // SL for an opened order extern double TakeProfit=35; // TP for an opened order extern double Lots =0.1; // Strictly set amount of lots extern double Prots =0.07; // Percent of free margin // a -- extern int Period_MA_1 =56; // Period of calculation MA extern int Bars_V =34; // Amount of bars for rate calculation extern int Aver_Bars =0; // Amount of bars for smoothing extern double Level =0.001; // b -- bool Work=true; // EA will work. string Symb; // Security name // int start() int Total, // Amount of orders in a window Tip=-1, // Type of selected order (B=0,S=1) Ticket; // Order number double MA_1_t, // Current MA_1 value MA_2_t, // Current MA_2 value Lot, // Amount of lots in a selected order Lts, // Amount of lots in an opened order Min_Lot, // Minimal amount of lots Step, // Step of lot size change Free, // Current free margin One_Lot, // Price of one lot Price, // Price of a selected order

257 SL, // SL of a selected order TP; // TP of a selected order bool Ans =false, // Server response after closing Cls_B=false, // Criterion for closing Buy Cls_S=false, // Criterion for closing Sell Opn_B=false, // Criterion for opening Buy Opn_S=false; // Criterion for opening Sell // // Preliminary processing if(bars > Period_MA_1) // Not enough bars Alert("Not enough bars in the window. EA doesn't work."); return; // Exit start() if(work==false) // Critical error Alert("Critical error. EA doesn't work."); return; // Exit start() // // Orders accounting Symb=Symbol(); // Security name Total=0; // Amount of orders for(int i=1; i>=orderstotal(); i++) // Loop through orders if (OrderSelect(i-1,SELECT_BY_POS)==true) // If there is the next one // Analyzing orders: if (OrderSymbol()!=Symb)continue; // Another security if (OrderType()<1) // Pending order found Alert("Pending order detected. EA doesn't work."); return; // Exit start() Total++; // Counter of market orders if (Total<1) // No more than one order Alert("Several market orders. EA doesn't work."); return; // Exit start() Ticket=OrderTicket(); // Number of selected order Tip =OrderType(); // Type of selected order Price =OrderOpenPrice(); // Price of selected order SL =OrderStopLoss(); // SL of selected order TP =OrderTakeProfit(); // TP of selected order Lot =OrderLots(); // Amount of lots // // Trading criteria int H= 1000; // Amount of bars in calc. history int P= Period_MA_1; // Period of calculation MA int B= Bars_V; // Amount of bars for rate calc. int A= Aver_Bars; // Amount of bars for smoothing // a -- double L_1=iCustom(NULL,0,"rocseparate",H,P,B,A,1,0); double L_5=iCustom(NULL,0,"rocseparate",H,P,B,A,5,0); // b -- if (L_5>=-Level && L_1<L_5) Opn_B=true; // Criterion for opening Buy Cls_S=true; // Criterion for closing Sell if (L_5<=Level && L_1>L_5) Opn_S=true; // Criterion for opening Sell Cls_B=true; // Criterion for closing Buy // // Closing orders

258 while(true) // Loop of closing orders if (Tip==0 && Cls_B==true) // Order Buy is opened.. // and there is criterion to close Alert("Attempt to close Buy ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Bid,2); // Closing Buy if (Ans==true) // Success :) Alert ("Closed order Buy ",Ticket); break; // Exit closing loop if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() if (Tip==1 && Cls_S==true) // Order Sell is opened.. // and there is criterion to close Alert("Attempt to close Sell ",Ticket,". Waiting for response.."); RefreshRates(); // Refresh rates Ans=OrderClose(Ticket,Lot,Ask,2); // Closing Sell if (Ans==true) // Success :) Alert ("Closed order Sell ",Ticket); break; // Exit closing loop if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() break; // Exit while // // Order value RefreshRates(); // Refresh rates Min_Lot=MarketInfo(Symb,MODE_MINLOT); // Minimal number of lots Free =AccountFreeMargin(); // Free margin One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// Price of 1 lot Step =MarketInfo(Symb,MODE_LOTSTEP); // Step is changed if (Lots < 0) // If lots are set, Lts =Lots; // work with them else // % of free margin Lts=MathFloor(Free*Prots/One_Lot/Step)*Step;// For opening if(lts > Min_Lot) Lts=Min_Lot; // Not less than minimal if (Lts*One_Lot < Free) // Lot larger than free margin Alert(" Not enough money for ", Lts," lots"); return; // Exit start() // // Opening orders while(true) // Orders closing loop if (Total==0 && Opn_B==true) // No new orders + // criterion for opening Buy RefreshRates(); // Refresh rates SL=Bid - New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Bid + New_Stop(TakeProfit)*Point; // Calculating SL of opened Alert("Attempt to open Buy. Waiting for response.."); Ticket=OrderSend(Symb,OP_BUY,Lts,Ask,2,SL,TP);//Opening Buy if (Ticket < 0) // Success :) Alert ("Opened oredr Buy ",Ticket); return; // Exit start() if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying

259 return; // Exit start() if (Total==0 && Opn_S==true) // No new orders + // criterion for opening Sell RefreshRates(); // Refresh rates SL=Ask + New_Stop(StopLoss)*Point; // Calculating SL of opened TP=Ask - New_Stop(TakeProfit)*Point; // Calculating SL of opened Alert("Attempt to open Sell. Waiting for response.."); Ticket=OrderSend(Symb,OP_SELL,Lts,Bid,2,SL,TP);//Opening Sels if (Ticket < 0) // Success :) Alert ("Opened order Sell ",Ticket); return; // Exit start() if (Fun_Error(GetLastError())==1) // Processing errors continue; // Retrying return; // Exit start() break; // Exit while // return; // Exit start() // int Fun_Error(int Error) // Function of processing errors switch(error) // Not crucial errors case 4: Alert("Trade server is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 135:Alert("Price changed. Trying once again.."); RefreshRates(); // Refresh rates return(1); // Exit the function case 136:Alert("No prices. Waiting for a new tick.."); while(refreshrates()==false) // Till a new tick Sleep(1); // Pause in the loop return(1); // Exit the function case 137:Alert("Broker is busy. Trying once again.."); Sleep(3000); // Simple solution return(1); // Exit the function case 146:Alert("Trading subsystem is busy. Trying once again.."); Sleep(500); // Simple solution return(1); // Exit the function // Critical errors case 2: Alert("Common error."); return(0); // Exit the function case 5: Alert("Old terminal version."); Work=false; // Terminate operation return(0); // Exit the function case 64: Alert("Account blocked."); Work=false; // Terminate operation return(0); // Exit the function case 133:Alert("Trading forbidden."); return(0); // Exit the function case 134:Alert("Not enough money to execute operation."); return(0); // Exit the function default: Alert("Error occurred: ",Error); // Other variants return(0); // Exit the function // int New_Stop(int Parametr) // Checking stop levels int Min_Dist=MarketInfo(Symb,MODE_STOPLEVEL);// Minimal distance if (Parametr<Min_Dist) // If less than allowed Parametr=Min_Dist; // Set allowed Alert("Increased distance of stop level.");

260 return(parametr); // Returning value // Elemezzük, hogy milyen módosítások történtek a tradingexpert.mq4 a forráskódjában. Az Expert Advisor fő részei alapvetően nem változtak. Két blokkban történtek változtatások - az1-2 blokkban és az 5-6 blokkban. Az 5-6 blokkban a kereskedési ismertetőjeleket számoljuk ki. A leírt EA-ban a kereskedési stratégia két kereskedési jelzést alkalmaz Buy nyitási és Sell zárási jelzést. Az Expert Advisorban használt stratégia csak egy nyitott piaci megbízást enged meg, függőben levő megbízások nem lehetnek. A stratégia szintén feltételezi a szemben fekvő megbízások zárását a nyitó kereskedési jelzéskor; például, ha az ismertetőjel egy Buy nyitását jelzi, ez azt is jelenti, hogy ha van Sell megbízás azt zárni kell. Hogy használni tudjuk a shared.mq4 EA-ban a rocseparate.mq4 egyéni indikátor számítási eredményeit, végre kell hajtani az icustom() függvényt: double L_1 = icustom(null,0,"rocseparate",h,p,b,a,1,0); double L_5 = icustom(null,0,"rocseparate",h,p,b,a,5,0); Ebben az esetben az icustom() formális paramétereinek a jelentése a következő: NULL az indikátorszámításokat az aktuális szimbólumablak adatai alapján hajtjuk végre, ebben az esetben az EA-t az EURUSD ablakhoz csatoltuk, úgyhogy EURUSD adatait fogjuk használni (lásd a 131. ábrát); 0 - számításokban az aktuális időkeret adatait használjuk; ebben az esetben az aktuális időkeret az M15, úgyhogy az M15 adatait fogjuk használni; "rocseparate" - az egyéni indikátor neve, amiben a számításokat fogjuk csinálni. H,P,B,A, - a beállítható paraméterek listája. Ebben az esetben a rocseparate.mq4 egyéni indikátornak külső beállítható paraméterei vannak (2-3 blokk a rocseparate.mq4 kódjában). A felhasználónak, hogy kezelni tudja ezeket a paramétereket az EA-ból, meg kell adnia az értékeiket az icustom () átadott paraméterek listájában. Az Expert Advisorban ezeknek a paramétereknek az értékei különbözhetnek azoktól, amelyeket az indikátorban megadtunk. Az indikátor a számítások alatt ezeket az átadott értékeket fogja használni. Ezek a paraméterek a következőt jelentik: H a számításokban résztvevő történelmi bárok száma; P - az MA számítás időszaka; B az árváltozás arány számításába résztvevő bárok száma; A - a simításban résztvevő bárok száma. (ezeknek a paramétereknek a jelentését a ROC egyéni indikátor (Price Rate of Change fejezetben részletesen elmagyaráztuk. 1 és 5 - az indikátorvonalak indexei. A rocseparate.mq4 egyéni indikátorban 6 indikátorvonalat használunk. Az aktuális ábrán ROC vonalat (narancs) a Line_1[] értékei alapján rajzoltuk, ezért az 1-es indexszel rendelkező buffer értékeit használtjuk. A simított átlagvonal a Line_5[] tömbelemek értékein alapul, ezért a felhasznált buffer indexe 5. 0 a felhasznált indikátorbuffer elem indexe (a shift a számított bárnak az aktuális bárhoz viszonyított helyzetét mutatja). Ebben az esetben a null bárban lévő indikátorvonalak értékeit használjuk, ezért a shift 0. Egy felhasználónak meg kell adni a lehetőséget, hogy kézzel is megváltoztassa az indikátor paramétereit az EA-n keresztül, ezt a célt szolgálják a külső változók az 1a-1b ( Expert Advisor) blokkban. 5-5 blokkban ezen paraméterek értékeit kapják az másik, rövidebb nevű változók ezt azért tesszük, hogy a kód az 5a-5b blokkban áttekinthetőbb legyen. Így meg tudja adni a felhasználó a shared.mq4 programon keresztül azokat a paramétereket, amelyekkel a rocseparate.mq4 egyéni indikátor a számításokat el fogja végezni. Az icustom() függvény végrehajtása után a visszaadott érték az lesz ami az adott indikátorvonal megadott paraméterekkel számolt értéke. A gyakorlati felhasználás során kényelmes, ha látjuk a szimbólumablakba azt az indikátort, amelynek a tömbelemeit használjuk az Expert Advisorban (131. ábra)

261 Ugyanakkor az icustom() függvény végrehajtása nem igényli az indikátor jelenlétét a szimbólumablakban, sem azt, hogy az indikátornak és az icustom() függvénynek azonos paraméter értékei legyenek. Az ICustom() végrehajtásához nincs szükség a megfelelő indikátor jelenlétére a szimbólumablakban. Az icustom() hívása semelyik alkalmazási programból nem vezet a megfelelő indikátor szimbólumablakhoz történő csatolásához. Egy indikátor csatolása egy ablakhoz nem vezet az icustom hívásához semmilyen alkalmazási programban. Az EA kereskedési ismertetőjeleit (az 5-6 blokkban) az icustom() függvény által visszaadott tömbelem értékek alapján határozzuk meg. Például a Buy nyitó és Sell záró feltétele a következő: if (L_5<=-Level && L_1>L_5) Opn_B = true; // Criterion for opening Buy Cls_S = true; // Criterion for closing Sell Ha az (L_5) utolsó ismert értéke kevesebb a megadott szintnél (az állítható Level = paraméter értékénél) és a ROC vonal utoljára ismert értéke az aktuális időkeretben (L_1) nagyobb, mint az (L_5), akkor ezt az eseményt Buy nyitási és Sell zárási jelzésnek tekintjük. A szemben lévő megbízások vizsgálatára más feltételeket használunk. Az ebben a példában alkalmazott kereskedési ismertetőjeleket csak oktatási célra mutattuk be, és nem szabad irányelvként használni egy éles számlán

262 Beépített függvények Az MQL4 a technikai indikátorok függvényit leszámítva több mint 220 beépített függvényt tartalmaz. Itt lehetetlen mindegyiket példákkal bemutatni mert túl sok van belőlük. Néhány függvényt, amire szükségünk volt az előző részekben már részletesen bemutattunk. Ebben a részben foglalkozni fogunk a legtöbb, széles körben használt függvénnyel. Mindegyik bekezdés végén látni fogjuk az adott kategória függvényeinek a teljes listáját és a rövid leírásukat. Általános függvények Ez a csoport olyan függvényeket tartalmaz, amik nem sorolhatók semelyik, speciális csoportba. Ezek a következő függvények: Print(), Alert(), Comment(), MarketInfo(), Sleep() stb. Grafikus objektumok A MetaTrader 4 Terminal lehetöséget biztosít grafikus objektumok elhelyezésére. Ez a csoport olyan függvényeket tartalmaz amelyekel a program létrehozhatja ezen objektumokat, valamint megváltoztathatja a tulajdonságaikat mozgatja és törölheti ezeket. Chart műveletek Ez azon függvények csoportja, amelyek azon ábrával kapcsolatban szolgáltatnak különböző információkat, amihez az MQL4 programot (script, indikátor vagy Expert Advisor) hozzácsatoltuk. String függvények A string függvényeket a string típusú változókkal végzett munka során használjuk: értékek keresésére, a krkterek összefűzésére, a karaktersor részletenek vizsgálatára, stb. A konverziós függvények feladata, hogy segítségükkel átalakítsuk az egyik típus változókat egy másik változó típussá. A NormalizeDouble() függvény egy megadott pontosságra kerekíti a double típusú változók értékeit. Dátum és idő Ezt a függvénycsoportot a dátum és idő információval végzett műveletekre használjuk: a LocalTime() függvény a számítógép helyi idejét adja vissza, a TimeCurrent() mutatja az utoljára ismert szerveridőt. Azonkívül segítségükkel megtudhatjuk hogy egy időpont az év, hónap, hét melyik napjára, illetve a nap melyik órájára és az óra melyik percére esik stb. Fájlműveletek Ezekkel a függvényekkel adatokat olvashatunk és írhatunk a merevlemezen. Tömbök és az előre definiált tömbök Hozzáférést nyújtanak minden elérhető szimbólum minden időszakának áradatához. Matemetikai függvények Alapvető matematikai és trigonometrikus függvények. A globalis változók függvényei A globális változókkal végzett munka függvényei. Egyéni indikátorok Ezeket a függvényeket kizárólag az egyéni indikátorok írásakor használjuk. Számla információ Ezek a függvények az ügyfélterminállal kapcsolatos információt mutatják, ellenőrzik az ügyfélterminál aktuális állapotát (beleértve az MQL4-programok végrehajtási körülményeit). Kereskedelmi függvények A kereskedelmi műveleteket végrehajtó függvények

263 Általános függvények A leggyakrabban használt függvény a Comment(). A Comment() függvény void Comment(...) Ez a függvény egy, a felhasználó által meghatározott üzenetet jelenít meg az ablak bal felső sarkában. A paraméterek bármilyen típusúak lehetnek. A paraméterek száma nem haladhatja meg a 64-et. Tömbök nem lehetnek a Comment() függvény paraméterei. A tömböket elemenként kell megadni paraméterként. A double típus adatait 4 tizedes számjeggyel írja ki a függvény. Nagyobb pontosságú megjelenítésre a DoubleToStr() függvényt használjuk. A bool, datetime és színtípusokat számértékükkel fogja kiírni. A datetime típus stringként történő megjelenítésére használjuk a TimeToStr() függvényt. Paraméterek: - bármilyen érték vesszőkkel elválasztva. Példa a függvény használatára ez az egyszerű comment.mq4 EA lehet, amellyel megjelenítjük a megbízások számával kapcsolatos információt. // // comment.mq4 // The code should be used for educational purpose only. // int start() // Special function start int Orders=OrdersTotal(); // Number of orders if (Orders==0) // If numb. of ord. = 0 Comment("No orders"); // Comment to the window corner else // If there are orders Comment("Available ",Orders," orders." );// Comment return; // Exit // A programban az OrdersTotal() függvénnyel meghatározzuk a megbízások számát. Ha az Orders változó (a megbízások száma) egyenlő 0-val a Comment() függvényt a No orders paraméterrel hajtjuk végre. Ha legalább egy megbízás van, Comment() függvényt vesszővel elválasztott paraméterlistával fogjuk végre hajtani. Ebben az esetben 3 paramétert használunk: az első az Available string állandó, a második az Orders változó értéke és a harmadik as orders string állandó. A start() függvény mindegyik végrehajtásánál az üzenet meg fog jelenni a chart ablak felső bal sarkában. A 132. ábrán azt a variációt láthatjuk, amikor egy megbízásunk van ábra. Egy szöveges üzenet az ablak bal felső sarkban a comment script végrehajtása után. A hangállományok lejátszására PlaySound() függvényt használjuk

264 A PlaySound() függvény void PlaySound(string filename) A függvény egy hangállományt játszik le. A fájlnak a terminal_directory\sounds alkönyvtárában kell elhelyezkednie. Paraméterek: filename - a hangállomány elérési útja. Néhány ajánlott hangállományt mellékeltek a programhoz - Sound Files. Néha a programkód írása során felmerül a felhasználóval fojtatott párbeszéd szükségessége. Az MessageBox() függvényt használjuk erre a célra. A MessageBox() függvény int MessageBox(string text=null, string caption=null, int flags=empty) Paraméterek: text a bemutatott üzenet szövege; caption az üzenetdoboz fejlécének szabadon megválasztható szövege. Ha ez a paraméter hiányzik, az EA neve fog megjelenni a dobozfejlécben; flags - szabadon megválasztható flagek, amik meghatározzák a dialógusdoboz fajtáját és viselkedését. A különböző flagek kombinálni is (lásd:messagebox Return Codes). Nézzünk egy példát a MessageBox() használatára! 31. feladat: Írd meg annak az EA-nak a kódját, amelyik mutat egy üzenetdobozt azzal a kérdéssel, hogy a fontos sajtóközleményt megelőzően 5 percben bezárjon-e minden megbízást. Ha a felhasználó a Yes-re kattint, minden megbízást zárni fog, ha a No-ra akkor semmilyen kereskedelmi műveletet nem hajt végre. A felhasználóval párbeszédet fojtató EA kódja a következő (dialogue.mq4): // // dialogue.mq4 // The code should be used for educational purpose only. // #include <WinUser32.mqh> // Needed to MessageBox extern double Time_News=15.30; // Time of important news bool Question=false; // Flag (question is not put yet) // int start() // Special function start PlaySound("tick.wav"); // At each tick double Time_cur=Hour()+ Minute()/100.0;// Current time (double) if (OrdersTotal()>0 && Question==false && Time_cur<=Time_News-0.05) // Providing some conditions PlaySound("news.wav"); // At each tick Question=true; // Flag (question is already put) int ret=messagebox("time of important news release. Close all orders?", "Question", MB_YESNO MB_ICONQUESTION MB_TOPMOST); // Message box // if(ret==idyes) // If the answer is Yes Close_Orders(); // Close all orders return; // Exit // void Close_Orders() // Cust. funct. for closing orders Alert("Function of closing all orders is being executed.");// For illustration

265 return; // Exit // A 1-2 blokkban a WinUser32.mqh fájlt illesztjük be a programba; ebben a fájlban a MessageBox() visszatérési kódokat határoztuk meg. Valamint ebben a blokkban a külső Time_news változóban beállítjuk - a fontos sajtóközlemény idejét. Az EA végrehajtásának egész időszaka alatt a megbízások zárásával kapcsolatos kérdést csak egyszer kell feltenni. Annak számontartására, hogy a kérdést feltettük-e már, az EA Question változót használjuk. A start() különleges függvény mindegyik kezdésénél (a 2-3 blokkban) a PlaySound() függvényt végrehajtjuk. A lejátszott a tick.wav hang egy gyenge kattanásra hasonlít, ami egy új tick megjelenését jelzi. A programban lejátszott hangot a programozó választja meg. Néhány esetben nagyon hasznos a hangokat használni. Például egy hangjelzés jelentheti egy EA végrehajtását. Más hangok egyéb eseményeket jelezhetnek, például: kereskedési ismertetőjel megjelenése, megbízás zárása, stb. A Time_cur változó értéke az aktuális szerveridő értékét veszi fel. Az EA-ban a párbeszéd ablak megnyitási feltételeinek elemzése történik. Ha van egy vagy több megbízásunk, és az üzenetdobozt eddig még nem jelenítette meg a program és a szerveridő valamint a fontos sajtóközlemény időpontja közötti idő kevesebb mint 5 perc, akkor bizonyos műveleteket fog végrehajtani a program. Először is a PlaySound() végrehajtása a hangjelzéssel felkelti a felhasználó figyelmét. A Question változó true értéket kap (ezért nem kell többször mutatni a szövegdobozt). A következő sorban végrehajtjuk a MessageBox() függvényt: int ret=messagebox("time of important news release. Close all orders?", "Question", MB_YESNO MB_ICONQUESTION MB_TOPMOST); // Message box Ebben az esetben a beállított string állandó értéke ("Time of important news release. Close all orders?") fog megjelenni a szövegdobozban, valamint a szövegdoboz fejlécében a Question érték. Az MB_YESNO flag jelenti a Yes és No gombok jelenlétét (lásd: MessageBox Return Values). Az MB_ICONQUESTION flag meghatározza azt az ikont, amit az üzenetdoboz bal oldali részében láthatunk, (minden működési variációnak van saját ikonja a Windows XP ikonkészletből). Az MB_TOPMOST flag meghatározza, hogy a szövegdoboz mindig látható legyen, mindig felül függetlenül attól milyen programokat hajtunk végre pillanatnyilag a számítógépen. MessageBox() végrehajtása következtében a megadott paraméterekkel egy üzenetdobozt láthatunk: Fig Dialog box displayed as a result of MessageBox() execution. Amikor az üzenetdoboz megjelenik, a program végrehajtása megáll addig, amíg a felhasználó egy gombra nem kattint az üzenetdobozban. Amint ez megtörténik, a vezérlés át fog kerülni a MessageBox() hívását követő programsorba, ebben az esetben a 3-4 blokkba. Egy üzenetdoboznak az a tulajdonsága, hogy magánál tartja a vezérlést nagyon fontos, és ezt számításba kell venni a programfejlesztésnél. Például ha a felhasználó elhagyta a számítógépét és az üzenetdoboz ez alatt az idő alatt jelenik meg, a program válaszra fog várni és kódot nem fogja végrehajtani az alatt az időszak alatt (amíg valamelyik gombot meg nem nyomjuk). Megjegyzés: mielőtt az üzenetdoboz megjelenik a programvégrehajtást a tickeket jelző hang kíséri. Amikor az üzenetdoboz megjelenik, azt egy másféle hang jelzi. Abban az időszakban, amikor az üzenetdoboz a válaszra várva nyitva van, nincs hangjelzés, amely jelezné, hogy a vezérlés a nyitott dialógusablak válaszára vár. Miután egy gombot megnyomtunk, a program végrehajtása folytatódni fog és a tickek hangját megint hallani fogjuk. Ha a felhasználó a Yes-re kattint, a Close_Orders() függvényt fogja hívni a program és ez a függvény a megbízásokat be fogja zárni. A Close_Orders() függvény leírása itt nem szerepel, a végrehajtás tényét az Alert() függvény jelzi: ("Function of closing all orders is being executed."). Ha a felhasználó a No-ra kattint, a megbízásokat lezáró függvényt nem hívjuk. Az EA aktuális munkamenete alatt az üzenetdoboz nem fog megint megjelenni

266 Általános függvények Függvény Alert Comment Összefoglaló információ Bemutat egy üzenetet, ami a felhasználói által meghatározott adatokat tartalmaz. Paraméterei lehetnek bármilyen típusúak. A paraméterek szám nem lehet több 64-nél. Megjeleníti a felhasználó által meghatározott üzenetet a szimbólumablak bal felső sarkában. A paraméterek bármilyen típusúak lehetnek. A paraméterek szám nem múlhatja felül a 64-et. GetTickCount A GetTickCount() milliszekundumokban visszaadja a rendszer elindítása óta eltelt időt. A számlálót a rendszeridőzítő kapacitása korlátozza. Mivel az időt előjel nélküli egészként tároljuk, a számláló 49.7 naponként túlcsordul (lenullázódik). MarketInfo MessageBox PlaySound Print SendFTP SendMail Sleep Visszaadja a Piacfigyelő ablakban lévő szimbólumokkal kapcsolatos különféle információkat. Az információ aktuális szimbólumra vonatkozó részét előre definiált változókban tárolja (lásd: MarketInfo() Identifiers). A MessageBox függvény létrehoz és megjelenít egy dialógusablakot, valamint kezeli azt. Egy dialógusablak egy fejlécet és egy program által meghatározott üzenetet tartalmaz, valamint az előre definiált ikonok és nyomógombok meghatározott kombinációját. Ha a függvényt sikeresen végrehajtjuk, a visszatérési érték a MessageBox() függvény visszatérési kód-értékei közül az egyik lesz. A függvényt nem hívhatjuk egy egyéni indikátorból, mert az indikátorokat az interfész felületen hajtjuk végre és nem lassíthatjuk azt a válasz várásával. Egy hangállományt játszik le. A fájlnak a terminal_dir\sounds könyvtárban vagy annak az alkönyvtárában kell elhelyezkednie. Egy bejegyzést ír az expert logba. A paraméterek bármilyen típusúak lehetnek. A paraméterek száma nem lehet több 64-nél. Egy fájlt küld arra a címre, amit megadtunk az Eszközök-> Beállítások -> Kiadó fülön. Ha a kísérlet nem sikerül a visszaküldött érték FALSE. A függvény nem működik tesztelő módban. Ezt a függvényt nem hívhatjuk egyéni indikátorokból. A küldendő fájlnak a terminal_directory\experts\files mappában vagy annak almappájában kell lennie. A fájl nem kerül elküldésre, ha nincs FTP cím és/vagy hozzáférési jelszó a beállításokban megadva. Egy t küld arra a címre, amit a Beállítások fülén megadtunk. Az üzenetek küldését a beállításokban kikapcsolhatjuk, vagy az címet elhagyhatjuk. A Sleep() függvény felfüggeszti az aktuális expert végrehajtását a megadott időtartamra. A Sleep() nem hívható egyéni indikátorokból, mert az indikátorokat interfész felületen hajtjuk végre és nem lassíthatjuk őket

267 Grafikus objektumok A grafikus objektum egy kép a szimbólumablakban; azt kiválaszthatjuk, mozgathatjuk, módosíthatjuk vagy törölhetjük. Grafikus objektumok lehetnek például vízszintes és függőleges vonalak, lineáris regressziós csatornák, Fibonacci szintek, téglalapok, feliratok, stb. Bizonyos alakzatok: indikátorvonalak, indikátorszintek, gyertyák, a Comment() függvény megjegyzései nem választhatók ki és nem törölhetők, azért mert nem tartoznak a grafikus objektumok közé. A grafikus objektumokat az ügyfélterminál rajzolja a szimbólumablakba előre beállított koordinátákkal. Minden grafikus objektumnak a típusától függően van egy, két vagy három koordinátája és egyéb beállítható paramétere. Kézzel az ablakba tehetünk bármilyen grafikus objektumot (az eszköztár menüből), valamint elhelyezhetjük őket az ugyanabban az ablakban elindított Expert Advisor, script vagy egyéni indikátor végrehajtásának eredményeképpen is. A grafikus objektumok típusát és helyét módosíthatjuk kézzel vagy egy programmal ami a új koordináták és más paramétereket küldi a grafikus objektumnak. A grafikus objektumok elhelyezésének módszere Az MQL4-ben a grafikus objektumok elhelyezésének két módszere van: a grafikonhoz viszonyított és az ablakhoz viszonyított elhelyezés. A két módszer közti különbség bemutatására helyezzünk el kézzel két objektumot az ablakban: egy szöveget (OBJ_TEXT) és egy szöveg címkét (OBJ_LABEL). Használjuk az A és T gombokat az ügyfélterminál eszköztárán. Állítsuk be az ablakméretet a képernyőméret felére (134. ábra). Lássuk, hogy ezek a grafikus objektumok hogyan fognak reagálni az ablakméret-vált