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 " // // 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 " 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 ( 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 ( 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 " // // 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 " // #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 " // #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 " // // 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áltozásokra (és az árábra vízszintes és függőleges skálázására) ábra. Grafikus objektumok egy szimbólumablakban a pozíció meghatározás különböző módszereivel elhelyezve. A pozíció az ablakhoz rögzített Az OBJ_LABEL grafikus objektum mozdulatlan fog maradni, ha az ablakot a jobboldali vagy az alsó szegély elmozdításával méretezzük át. De ha az ablakméretet a felső vagy a bal oldali szegély mozgatásával változtatjuk meg, akkor ezek a grafikus objektumok is elmozdulnak, de az objektumok pozíciója ezekhez a szegélyekhez képest változatlan maradi. Ez azért történik így, mert az OBJ_LABEL helyzetét az ablakhoz viszonyítjuk. Ebben az esetben a grafikus objektum viszonyítási pontja az ablak bal felső sarka. Az objektum koordinátái a referencia ponthoz viszonyítva 193 és 48 pixel (135. ábra)

268 listájáról). A következő három nulla közül az első azt jelenti, hogy az objektumot a fő ablakban kell létrehozni (a fő ablaknak ahol a grafikon van az indexe 0) ábra. Az OBJ_LABEL grafikus objektum beállításai. - Az objektum referencia pontja (ebben az esetben) a kurzorkeret bal felső sarkában található, amikor az egérrel az objektumot kijelöljük. A kurzorkeret bal felső sarkában láthatunk egy pontot, ennek a pontnak a pozíciója jelenti a grafikus objektum pozícióját. A szimbólum ablak többi sarkát is kijelölhetjük az objektumok referencia pontjaként. Amikor az új bárok megjelennek az ablakban, OBJ_LABEL objektum helyzete az ablakhoz képest változatlan marad. Az ilyen objektumok kényelmesen használhatók az általános információk szöveges kijelzéséhez, például kereskedelemi megbízásokkal kapcsolatos információk, a megbízások távolságának legkisebb értéke, stb. A pozíció a grafikonhoz rögzített Az ablak méretének bármilyen módszerrel történő megváltoztatása, és a grafikon zoomolása nem fogja az OBJ_TEXT típusú objektumok helyzetét megváltoztatni. Az ilyen objektumok referencia pontja a kurzorkeret felső határoló vonalának a közepe, az X koordináta az idő, az Y koordináta pedig az ár (136. ábra) ábra. Az OBJ_TEXT grafikus objektum beállításai. Amikor az új bárok megjelennek az ablakban, OBJ_TEXT pozíciója a grafikonhoz viszonyítva nem változik, vagyis az új bárok megjelenésével az objektum a grafikonnal együtt el fog mozdulni balra; és egy idő után, amikor már elég bár lesz, az objektum tovább fog mozogni balra az ablakhatáron túl

269 A kétféle objektumtípus helyzet-meghatározási módszere az objektumtípusok jellemző tulajdonságai és azt nem tudja megváltoztatni sem a felhasználó sem valamelyik program. A grafikus objektumok többségét az időhöz és árhoz viszonyítva helyezzük el. Grafikus objektumok létrehozása és a tulajdonságaik megváltoztatása A grafikus objektumok létrehozása az előre definiált objektumtípusok (lásd: A grafikus objektumtípusok és azok tulajdonságai) szimbólum ablakba helyezését jelenti. Az objektumok létrehozására a következő függvényt használjuk: ObjectCreate() függvény bool ObjectCreate(string name, int type, int window, datetime time1, double price1, datetime time2=0, double price2=0, datetime time3=0, double price3=0) A függvény létrehozza az előre beállított névvel és típussal rendelkező objektumot meghatározott koordinátákkal a meghatározott segédablakban. A koordináták száma az objektumtípustól függően 1-3 lehet. Ha egy objektumot sikeresen létrehozott, a függvény TRUE-t küld vissza, más esetben a visszaküldött érték FALSE. További információt kapunk a hibáról a GetLastError() függvény hívásával. A koordinátákat idő és ár párokban kell megadni. Például az OBJ_VLINE-nak egyedül az idő koordinátára van szüksége, de árat szintén meg kell adni (bármilyen érték lehet). Az OBJ_LABEL típusú grafikus objektumoknak a függvényben megadott koordinátái figyelmen kívül lesznek hagyva, ha az ObjectSet() függvénnyel beállítjuk az OBJPROP_XDISTANCE-t és OBJPROP_YDISTANCE értékeit. Paraméterek: name az objektum neve; type - objektumtípus (egy típus az előre definiált objektumtípusok közül); window - annak az ablaknak a száma, amelyik ablakhoz az objektumot hozzá fogjuk adni. A segédablakok számozása (ha indikátor segédablakok vannak) 1-el kezdődik, a fő ablak száma mindig 0; a megadott ablak számának nagyobbnak vagy egyenlőnek kell lennie, mint 0, és kisebbnek a WindowsTotal() függvény visszatérési értékénél; time1 - az első koordináta idő értéke; price1 - az első koordináta ár értéke; time2 - a második koordináta idő értéke; price2 - a második koordináta ár értéke; time3 - a harmadik koordináta idő értéke; price3 - a harmadik koordináta ár értéke. Mindegyik grafikus objektumnak vannak további (rá jellemző) beállítható paraméterei. Például a koordináták megadásán túl megadhatjuk a színt, az üzenet szöveget ( néhány objektumnál), vonalstílusokat (más objektumoknál), stb. A tulajdonságok megváltoztatásához használjuk a következő függvényt: ObjectSet() függvény bool ObjectSet(string name, int prop_id, double value) A függvény megváltoztatja a megadott objektum tulajdonságát. Ha ez sikerül a függvény TRUE-t küld vissza, más esetben FALSE-t. Hibainformációt a GetLastError() függvény hívásával kapunk. Paraméterek:

270 name az objektum neve; prop_id az objektum tulajdonság azonosítója (az objektum tulajdonságokat jezi); value - a jelezett tulajdonság új értéke. Minden grafikus objektumnak lehet egy felirata. Az objektumok feliratát megváltoztathatjuk az objektum tulajdonságok eszköztárán keresztül, vagy programozott módon. OBJ_TEXT-nek és az OBJ_LABEL-nek ez a felirat a fő tartalmuk és mindig látható, a többi objektum felirata az objektum közelében látható, ha a "Show object descriptions" opció ezt lehetővé teszi a tulajdonságok ablakban (F8). A feliratok megváltoztatására a következő függvényt használjuk: ObjectSetText() függvény name, string text, int font_size, string font_name=null, color text_color=clr_none) A függvény segítségével megváltoztathatjuk egy objektum feliratát. A siker végrehajtás esetében TRUE-t kapunk vissza, egyébként FALSE-t. Hibainformációt a GetLastError() függvény hívásával kapunk. A font_size, font_name és text_color paramétereket csak az OBJ_TEXT és OBJ_LABEL esetén használjuk. Az egyéb típusú objektumoknál ezeket a paramétereket figyelmen kívül hagyja a terminál. Paraméterek: name az objektum neve; text a felirat szövege; font_size a betűméret pontokban; font_name a betűtípus neve; text_color - szövegszín. Elemezzünk egy példa Expert Advisort, amiben a fontosabb grafikus objektum függvényeket használjuk. 32. feladat: Grafikus objektumok segítségével tájékoztassuk a felhasználót a MACD alapján meghatározott kereskedési ismertetőjelekről. A MACD indikátort nagyon gyakran használják a kereskedők a kereskedési ismertetőjelek előállítására. Az indikátort két vonal képviseli a fő vonal és a signal vonal. Kereskedési jelzések akkor keletkeznek, amikor a szóban forgó vonalak kereszteződnek. Ha a fő indikátorvonalat (általában szürke hisztogram) lefelé keresztezi a szignálvonal (általában piros pontozott vonal), ez egy eladási jelzés, ha felfelé - vételi. A vonalak kereszteződése közötti időben a megbízásokat nyitva kell tartani, és mikor egy ellentétes ismertetőjel keletkezik, a megbízásokat zárni kell, ugyanakkor egy szemben lévő megbízást kell nyitni. Így négy üzenettípust kell lekereskedni: Buy nyitás, Sell nyitás, Buy tartása és Sell tartása. Ebben a feladatban az üzenetek kölcsönösen kizárják egymást, egyszerre lehetetlen kettőt vagy több üzenet megjelenítése. Ezért ebben az esetben egy grafikus objektumot használhatunk; az objektum mindig jelen lessz a képernyő, de azt időről időre meg fogjuk változtatni. Helyezzük el ezt az objektumot annak az ablaknak a jobb felső sarkába, amiben az EA működni fog! Mivel az felirat helyzetét nem kell megváltoztatni, célszerű az OBJ_LABEL az típust használni, mert ennek a helyzete az ablakhoz viszonyítva állandó. Oldjuk meg a 32 feladatot a grafobjects.mq4 EA segítségével, miközben az OBJ_LABEL grafikus objektumot használjuk: // // grafobjects.mq4 // The code should be used for educational purpose only. //

271 int start() // Special function start // int Sit; double MACD_M_0,MACD_M_1, // Main line, 0 and 1st bar MACD_S_0,MACD_S_1; // Signal line, 0 and 1st bar string Text[4]; // Declaring a string array color Color[4]; // Declaring an array of colors Text[0]= "Opening of Buy"; // Text for different situations Text[1]= "Opening of Sell"; Text[2]= "Holding of Buy"; Text[3]= "Holding of Sell"; Color[0]= DeepSkyBlue; // Object color.. Color[1]= LightPink; //.. for different situations Color[2]= Yellow; Color[3]= Yellow; // ObjectCreate("Label_Obj_MACD", OBJ_LABEL, 0, 0, 0);// Creating obj. ObjectSet("Label_Obj_MACD", OBJPROP_CORNER, 1); // Reference corner ObjectSet("Label_Obj_MACD", OBJPROP_XDISTANCE, 10);// X coordinate ObjectSet("Label_Obj_MACD", OBJPROP_YDISTANCE, 15);// Y coordinate // MACD_M_0 =imacd(null,0,12,26,9,price_close,mode_main,0); // 0 bar MACD_S_0 =imacd(null,0,12,26,9,price_close,mode_signal,0);// 0 bar MACD_M_1 =imacd(null,0,12,26,9,price_close,mode_main,1); // 1 bar MACD_S_1 =imacd(null,0,12,26,9,price_close,mode_signal,1);// 1 bar // // Analyzing situation if(macd_m_1=macd_s_0) // Crossing upwards Sit=0; if(macd_m_1>macd_s_1 && MACD_M_0<=MACD_S_0)// Crossing downwards Sit=1; if(macd_m_1>macd_s_1 && MACD_M_0>MACD_S_0) // Main above signal Sit=2; if(macd_m_1 Az EA 1-2 blokkjában Text[] és Color[] tömbök elemértékeit határozzuk meg. A későbbiekben ezeket a paramétereket az objektum tulajdonságainak megváltoztatására használjuk. A 2-3 blokkban létrehozzuk az objektumot és valamennyi tulajdonságát beállítjuk. Elemezzük ezt a blokkot részletesen! Ebben az EA kódrészben egy grafikus objektumot hozunk létre abban az ablakban, amiben az EA-t végrehajtjuk: ObjectCreate("Label_Obj_MACD", OBJ_LABEL, 0, 0, 0);// Creating obj. A "Label_Obj_MACD" a létrehozandó objektum neve (az objektum nevét a programozó saját belátása szerint választhatja meg). Az OBJ_LABEL az objektum típusának azonosítója, ezzel az azonosítóval választjuk ki az objektum típusát (a lehetséges típusok listájáról). A következő három nulla közül az első azt jelenti, hogy az objektumot a fő ablakban kell létrehozni (a fő ablaknak ahol a grafikon van az indexe 0). A következő két nulla beállítja a létrehozandó objektum koordinátáit. E szerint a koordináták szerint fog az objektum az ablakban elhelyezkedni. Ebben az esetben a létrehozott OBJ_LABEL nem használ időt és árkoordinátákat. Jegyezd meg: az OjectCreate() leírásában csak idő és árkoordinátákat adhatunk meg. Azon túl, a második és harmadik koordináta pároknak alapértelmezett értékeik vannak, míg az első koordináta párnak alapértelmezett értékei nincsenek. Ez azt jelenti, hogy bár OBJ_LABEL objektumnak egyáltalán nincs szüksége idő és árkoordinátákra, az ObjectCreate() függvényhívásban valamilyen értéket meg kell adni. Ebben az esetben nullákat írtunk, de bármilyen más értékeket írhatnak, ezeket az értékeket az OBJ_LABEL létrehozásakor nem vesszük figyelembe. A következő három sorban a korábban létrehozott Label_Obj_MACD néhány tulajdonságát állítjuk be:

272 ObjectSet("Label_Obj_MACD", OBJPROP_CORNER, 1); // Reference corner ObjectSet("Label_Obj_MACD", OBJPROP_XDISTANCE, 10);// X coordinate ObjectSet("Label_Obj_MACD", OBJPROP_YDISTANCE, 15);// Y coordinate A referencia sarok (OBJPROP_CORNER) beállítása 1, ami azt jelenti, hogy koordinátákat a korábban meghatározott fő ablak jobb fölső sarkához viszonyítjuk. A referencia saroktól atávolságot pixelekben határoztuk meg : a vízszintes távolság (OBJPROP_XDISTANCE) 10 pixel és a függőleges (OBJPROP_YDISTANCE) 15 pixel. A programvégrehajtás jelen állapotában az objektumot már létrehoztuk, van azonosító neve és meghatároztuk a fő tulajdonságait. Azért, hogy a felirat a megfelelő szöveget jelenítse meg, el kell végeznünk az ehhez szükséges számításokat. A 3-4 blokkban vizsgáljuk a MACD vonalak pozícióját az aktuális és az előző bárokon, azután 4-5 blokkban a meghatározzuk a Sit változót aktuális helyzetnek megfelelően (lásd: 107. ábra és callstohastic.mq4). A következő sorban az aktuális helyzettől függő objektum tulajdonságokat határozzuk meg: // Changing object properties ObjectSetText("Label_Obj_MACD",Text[Sit],10,"Arial",Color[Sit]); Az ObjectSetText() függvény végrehajtása után, ami a felirat szövegét meghatározza, az Label_Obj_MACD objektum szövege a Text[Sit] string változó értéke lesz. Ez az érték a különböző helyzetekben más lesz, és a Sit változó értékétől függ. Például, ha a 4-5blokkban a fő vonal lefelé keresztezi a szignál vonalat, a Sit értéke 1 lesz és a grafikus objektum szövege a Text[1] tömbelem lesz: "Opening of Sell". A többi paraméter: 10, Arial és Color[Sit] betűméretet, a felirat betűinek nagyságát, típusát és színét határozza meg. Az EA végrehajtásnak eredménye a következőképpen fog megjelenni az EURUSD ablakban: 137.ábra. A grafobjects.mq4 EA végrehajtásának az ereménye a sell jelzés megjelenésekor ábrán a főablak és egy MACD kiegészítő ablak látható. Tudjuk, hogy az EA működéséhez ennek az indikátornak a jelenléte nem szükséges, mert az EA-ban a kereskedési ismertetőjelek kiszámításához szükséges adatokat a a technikai indikátor függvény végrehajtásával szerezzük, ami nem áll kapcsolatban az indikátor megjelenítésével. Itt az indikátort csak a kereskedési ismertetőjelek meghatározásának vizuális bemutatására indítottuk el. Az EA hasonló módon fog működni minden más kombinációnál, mindig az indikátorvonalak kölcsönös helyzetének megfelelő feliratot látjuk. A grafikus objektumok törlése

273 A vizsgált grafobjects.mq4 Expert Advisornak van egy kis szépséghibája. Miután az EA abbahagyja működés, ez a grafikus objektum ott fog maradni az ablakban (a tulajdonságai a legutoljára kapott tulajdonságok lesznek). A grafikus objektumok nem törlődnek automatikusan. Később az "Opening of Sell" üzenet nem lesz érvényes. Azért, hogy ne tájékoztassuk félre a felhasználót, a grafikus objektumokat törölni kell. A grafikus objektumot a törléséhez (függetlenül attól, a program vagy a felhasználó hozta létre) egyszerűen kijelöljük és megnyomjuk a Delete gombot. Azonban a helyesen megírt programnak meg kell tisztítania az ablakot, amikor bezárják. Más szóval, egy programnak tartalmaznia kellene egy olyan blokkot, ahol minden grafikus objektumot, amit létrehozott a program, a bezárásakor letöröl. ObjectDelete() függvény bool ObjectDelete(string name) Töröljük a megadott nevű objektumot. Ha egy objektumot sikeresen töröl, a függvény visszaküldi a TRUE-t minden más esetben FALSE-t. Hibainformációt a GetLastError() függvény hívásával kapunk. Paraméterek: name a törlendő objektum neve. Az ObjectDelete() nagyon könnyen kezelhető: egyszerűen meg kell adni a törlendő objektum nevét. Az előző példa hibáját kijavítandó, adjuk hozzá a grafobjects.mq4 EA-hoz a különleges deinit() függvényt, ahol az objektumok törlése megtörténik: // int deinit() // Special function deinit ObjectDelete("Label_Obj_MACD"); // Object deletion return; // Exit deinit() // Most az EA által létrehozott Label_Obj_MACD objektum törlődni fog. Általában egy program több objektumot hozhat létre. Mindegyiket ezen algoritmus szerint tudjuk törölni. Grafikus objektumok módosítása Néha szükséges lehet egy objektum helyének megváltoztatására programozási módszerrel. Nagyon gyakran ez az igény az új bárok megjelenésekor merül föl. Például, egy EA-ban kereskedési ismertetőjeleket egy lineáris regressziós csatorna alapján alakítjuk ki, amit bizonyos számú történelmi bár alapján hozunk létre (például az utolsó 50 bár). Ha egyszerűen rajzolunk egy lineáris regressziós csatornát az ablakban, az változatlan marad legfeljebb a grafikonnal együtt elmozdul balra, amikor az új bárok megjelennek. Hogy megakadályozzuk egy objektum elmozdulását, azt újra kell rajzolni mindegyik új bárnál. Ezért az objektum új koordinátáit ki kell számítani és meg kell adni, és ezekkel a koordinátákkal újra kell rajzolni a az objektumot. Hogy megtudjuk, az adott objektum milyen tulajdonságokkal rendelkezik, használjuk a következő függvényt: ObjectGet() függvény double ObjectGet(string name, int prop_id) A függvény visszaküldi a kért objektumtulajdonság értékét. Hibainformációt a GetLastError() függvény hívásával kapunk. paraméterek:

274 name az objektum neve; prop_id - tárgytulajdonság azonosító. Bármilyen érték lehet a tárgytulajdonságok listájáról. Új koordinátákat adhatunk egy objektumnak az ObjectMove() függvénnyel. ObjectMove() függvény bool ObjectMove(string name, int point, datetime time1, double price1) Megváltoztatja egy pont koordinátáját. Ha a műveletet végrehajtotta visszaküldi a TRUE-t, minden más esetben FALSE-t. Hibainformációt a GetLastError() függvény hívásával kapunk. Az objektum koordinátáinak a számozása 0-val kezdődik. Paraméterek: name az objektum neve; point a koordináta indexe (0-2); time1 az új időérték; price1 - új árérték. 33. feladat: Hozzunk létre egy programot (egy Expert Advisort) a lineáris regressziós csatorna rajzolásához az utolsó 50 báron. A lineáris regressziós csatorna létrehozásához csak két időkoordinátát használunk. Az árkoordinátákat (ha ilyeneket megadtunk a programban) az objektum létrehozása alatt nem veszi figyelembe az ügyfélterminál. A lineáris regressziós csatornát a történelmi áradatok alapján számítja ki az ügyfélterminál és ezért nem lehet megjeleníteni a diagramon kívül (indikátor segédablakban). Ezért az objektum árkoordinátája kötött (a terminál figyelmen kívül hagyja az árkoordinátákat) az az objektum saját állandó tulajdonsága. A moveobjects.mq4 Expert Advisor kezeli a grafikus objektum pozícióját a következő kód álapján: // // moveobjects.mq4 // The code should be used for educational purpose only. // extern int Len_Cn=50; // Channel length (bars) extern color Col_Cn=Orange; // Channel color // int init() // Special function init() Create(); // Calling user-def. func. of creation return; // Exit init() // int start() // Special function start() datetime T2; // Second time coordinate int Error; // Error code // T2=ObjectGet("Obj_Reg_Ch",OBJPROP_TIME2);// Requesting t2 coord. Error=GetLastError(); // Getting an error code if (Error==4202) // If no object :( Alert("Regression channel is being managed", "\n Book_expert_82_2. deletion prohibited.");

275 Create(); // Calling user-def. func. of creation T2=Time[0]; // Current value of t2 coordinate // if (T2!=Time[0]) // If object is not in its place ObjectMove("Obj_Reg_Ch", 0, Time[Len_Cn-1],0); //New t1 coord. ObjectMove("Obj_Reg_Ch", 1, Time[0], 0); //New t2 coord. WindowRedraw(); // Redrawing the image return; // Exit start() // int deinit() // Special function deinit() ObjectDelete("Obj_Reg_Ch"); // Deleting the object return; // Exit deinit() // int Create() // User-defined function.. //..of object creation datetime T1=Time[Len_Cn-1]; // Defining 1st time coord. datetime T2=Time[0]; // Defining 2nd time coord. ObjectCreate("Obj_Reg_Ch",OBJ_REGRESSION,0,T1,0,T2,0);// Creation ObjectSet( "Obj_Reg_Ch", OBJPROP_COLOR, Col_Cn); // Color ObjectSet( "Obj_Reg_Ch", OBJPROP_RAY, false); // Ray ObjectSet( "Obj_Reg_Ch", OBJPROP_STYLE, STYLE_DASH);// Style ObjectSetText("Obj_Reg_Ch","Created by the EA moveobjects",10); WindowRedraw(); // Image redrawing // A moveobjects.mq4 EA algoritmusa olyan, hogy az objektum, amit egyszer létrehoztunk a programvégrehajtás egész ideje alatt a képernyőn fog maradni. Ilyen esetben célszerű egy felhasználói függvény (ebben az esetben ez a Create() a 6-7 blokkban) használata az objektum létrehozására, és az a programból szükség esetén bármikor hívható. Az objektum felrajzolásához két időkoordináta megadása szükséges (T1 az objektum bal szélének, T2 pedig a jobb szélének az időkoordinátája): datetime T1 = Time[Len_Cn-1]; // Defining 1st time coord. datetime T2 = Time[0]; // Defining 2nd time coord. Ebben a példában az objektum jobb oldali határának mindig a null báron kell lennie, a második koordináta értékét a null bár nyitási idejéhez viszonyítjuk. A bal oldali koordinátát a felhasználó által beállított bárok száma (külső Len_Cn változó) alapján számoljuk ki, a megfelelő bár indexből meghatározzuk a bár nyitási idejét. Például, ha a csatorna hossza 50 bár, a bal oldali koordináta egyenlő lesz a 49-es indexű bár nyitási idejével. A Create() felhasználói függvény további soraiban az ObjectCreate() függvény segítségével létrehozzuk az OBJ_REGRESSION objektumot és az ObjectSet() függvénnyel beállítjuk a szükséges tulajdonságait (a külső változóban előre beállított színt, a rajzolás módját és a vonalstílust). Ebben a sorban: ObjectSetText("Obj_Reg_Ch","Created by the EA moveobjects",10); egy szöveges leírását adunk az objektumhoz. A korábban elemezett OBJ_LABEL-lel ellentétben az OBJ_REGRESSION szöveges leírása nem látható. A grafikus objektumok szöveges leírását megnézhetjük az objektum tulajdonságok fülén. Ez a gyakorlatban nagyon kényelmes, mert megkülönböztethetjük a kézzel és a programmal létrehozott objektumokat:

276 138. ábra. A moveobjects.mq4 EA által létrehozott lineáris regressziós csatorna grafikus objektum általános tulajdonságai. Egy további függvényt alkalmazunk az aktuális ábra újra rajzolásához: WindowRedraw(); // Image redrawing WindowRedraw() függvény void WindowRedraw() A függvény erőszakosan újrarajzolja az aktuális ábrát. Általában az objektumok paramétereinek megváltoztatása után használjuk. Általában a grafikus objektumokat az ügyfélterminál az új tick érkezésekor rajzolja meg. Ezért ha nem használjuk a WindowRedraw() függvényt, az objektum paraméterekben bekövetkező változások csak a következő ticknél válnak láthatóvá a felhasználó számára, vagyis a rajzolás mindig egy ticket késik. A WindowRedraw() használata lehetővé teszi, hogy erőszakosan újrarajzoljunk minden objektumot közvetlenül a tulajdonságai megváltoztatása után. Ha több objektum tulajdonságait is megváltoztattuk a programban, elegendő csak egyszer használni a WindowRedraw() függvényt, az utolsó objektum tulajdonság megváltoztatása után. A felhasználói függvényt először a különleges init() függvényből hívjuk. Amikor az EA-t csatoljuk a szimbólumablakhoz, az init() végrehajtása elkezdődik, melynek során a lineáris regressziós csatorna megjelenik a szimbólumablakban. Két lehetséges helyzetet vizsgálunk a start() függvényben: (1) az objektumot időközben törölte a felhasználó (3-4 blokk) és (2) az objektumot jobbra kell mozgatni, amikor egy új nulla bár kezdődik (4-5 blokk). A grafikus objektum meglétét úgy érzékeljük, hogy kérjük az egyik koordinátájának az értékét. Ha a tárgy létezik, az ObjectGet() függvény vissza fogja küldeni a kért koordináta értékét, a GetLastError() függvény pedig nulla értéket fog visszaküldeni (nem történt hiba a koordináta kérésekor). Azonban, ha a nevezett objektum nincs a szimbólumablakban, a GetLastError() függvény 4202 hibakódot fog vissza küldeni, az objektum nem létezik: T2=ObjectGet("Obj_Reg_Ch",OBJPROP_TIME2); // Requesting t2 coord. Error=GetLastError(); // Getting an error code Ha a hibaelemzés jelzi, hogy az objektum nem létezik, akkor a programnak létre kell hoznia azt, és a felhasználó helytelen beavatkozásáról üzenetet küld (a program nem töröl objektumokat, ezért azt csak a felhasználó törölhette). Az üzenet küldése után a program hívja a korábba említett Create() felhasználói függvényt, ami az objektumot újra létrehozza a szimbólumablakban

277 A következő blokk (4-5) végrehajtásának a kezdetén a grafikus objektum már létezik. Hogy eldöntsük, kelle azt mozgatni, ismernünk kell az aktuális pillanatban az objektum pozícióját. E céléból elegendő elemezni az objektum első koordinátájának a korábban szerezett értékét. Ha ez az érték nem esik egybe a null bár megnyitás idejével, új koordinátákat kell adni az objektumnak. A koordinátákat az ObjectMove() függvénnyel változtatjuk meg: ObjectMove("Obj_Reg_Ch", 0, Time[Len_Cn-1],0); //New t1 coord. ObjectMove("Obj_Reg_Ch", 1, Time[0], 0); //New t1 coord. Itt, az Obj_Reg_Ch első koordinátája (0 koordináta) a Time[Len_Cn-1] értékét fogja kapni, míg a második koordináta a Time[0] értéke lesz Az ObjectMove() függvényben az árkoordinátákat is meg kell adni, amelyeknek itt 0 értéket adtunk. Ezeket az ár paramétereket a függvényleírás szerint meg kell adni, de ebben az esetben ezeket figyelmen kívül fogja hagyni az ügyfélterminál. Ezen két programsor végrehajtása után a szóban forgó grafikus objektum tulajdonságai meg fognak változni. A WindowRedraw() függvény következő végrehajtása következtében az objektumot erőszakosan újra fogja rajzolni az ügyfélterminál - a koordináták új értékei szerint. Így, a start() függvény végrehajtása során a lineáris regressziós csatorna grafikus objektumot újra fogja rajzolni az ügyfélterminál minden új bár legelső tickjénél (lásd a 139. ábrát). Miután az EA végrehajtása véget ért, az adott grafikus objektumot a deinit() különleges függvény végrehajtása törölni fogja a szimbólumablakból (a program ki fogja söpörni a munkahelyét, miután a munkát befejezte) ábra. A lineáris regressziós csatorna a moveobjects.mq4 EA végrehajtásakor. Általános esetben valamennyi grafikus objektumot törölhetünk és létrehozhatunk a programban kiszámolt paraméterekkel. Jelezhetjük a támasz/ellenállás vonalakat (OBJ_TREND), függőleges vonalakkal jelezhetjük a fontos események közeledtét (OBJ_VLINE), előre vetíthetjük a jelenlegi ármozgásokat, feliratokat használhatunk (OBJ_LABEL és OBJ_TEXT), stb. Azonban tudni kell, sok esetben nem szükséges grafikus objektumokat használni. Például, ha nagyszámú azonos karaktert kell a képernyőn elhelyezni (például, nyilak), erre a célra használhatjuk az indikátorvonalak megfelelő módon beállított stílusait. Ez a megközelítés meg fog szabadítani a sok objektum koordinátájának kezelésétől, és megakadályozza a képek véletlen törlését (azokat a jeleket, amelyeket az indikátorok hoznak létre, nem lehet se kiválasztani se törölni)

278 Függvények a grafikus objektumokkal végzett munkához Függvény ObjectCreate ObjectDelete ObjectDescription ObjectFind ObjectGet Összefoglaló információ A függvény létrehozza az előre beállított névvel és típussal rendelkező objektumot, meghatározott koordinátákkal, a meghatározott segédablakban. A koordináták száma az objektumtípustól függően 1-3 lehet. Ha egy objektumot sikeresen létrehozott, a függvény TRUE-t küld vissza, más esetben a visszaküldött érték FALSE. A megadott nevű objektum törlése. A sikeres végrehajtás esetén a függvény TRUE-t küld vissza, minden más esetben FALSE-t. A függvény visszaküldi az objektum leírását. Az OBJ_TEXT és OBJ_LABEL típusok esetén a kiírt feliratot küldi vissza. A függvény megkeresi, hogy melyik ablakban van az adott objektum. A függvény visszaküldi az ablak indexét, ahol az objektumot megtalálta. Ha nem találja meg akkor -1-et küld vissza. A függvény visszaküldi az objektum adott tulajdonságának az értékét. ObjectGetFiboDescription A függvény visszaküldi a Fibo szintek leírását. A szintek száma attól függ, hogy az objektum melyik Fibo csoporthoz tartozik. A szintek maximális száma 32. ObjectGetShiftByValue ObjectGetValueByShift ObjectMove ObjectName ObjectsDeleteAll ObjectSet A függvény kiszámítja és visszaküldi annak a bárnak az indexét (az aktuális bárhoz viszonyítva) ahol az objektum árkoordinátája a megadott árral egyezik. A bárindex kiszámítását az első és második koordinátákra felállított elsőfokú egyenlettel végzi. A trendvonalakra és hasonló objektumokra használhatjuk. A függvény kiszámítja és visszaküldi az adott indexű báron (az aktuális bárhoz viszonyítva) az árkoordinátát. Az árérték kiszámítását az első és második koordinátákra felállított elsőfokú egyenlettel végzi. A trendvonalakra és hasonló objektumokra használhatjuk. Megváltoztatja egy objektum valamely koordinátáját a charton. Az objektumok helyzetét meghatározó pontok száma 1-3 lehet az objektum típusa szerint. Ha a végrehajtás sikerül TRUE-t küld vissza, ha nem akkor FALSE-t. A függvény az objektum sorszáma alapján visszaküldi az objektum nevét az objektum listából. Töröl minden adott típusú objektumot a megadott ablakból, vagy segédablakból. A függvény visszaküldi a törölt objektumok számát. A megadott objektum valamely tulajdonságának megváltoztatása. A siker esetén a függvény visszaküldi a TRUE-t, egyéb esetben FALSE-t. ObjectSetFiboDescription A függvény új leírást ad a Fibo szintnek. A szintek száma függ Fibonacci objektum típusától. A szintek száma max. 32 lehet. ObjectSetText ObjectsTotal ObjectType Megváltoztatja az objektum leírását. Az OBJ_TEXT és OBJ_LABEL leírását szövegként megjeleníti az ábrán. A siker esetén a függvény TRUE-t küld vissza, más esetben FALSE-t. Az ábrán lévő adott típusú objektumok számát küldi vissza. A függvény visszaküldi a jelezett objektum típusát

279 Műveletek a chartokkal A gyakorlatban egy kereskedő általában egy szimbólumablakban több segédablakot is kinyit, ahol indikátorokat helyez el. Korlátozások nincsenek az indikátorok számában, őket bármilyen sorrendben csatolhatjuk. Egy szimbólumablakban levő segédablakok száma sem korlátozott. Mindegyik segédablaknak van száma. A fő ablak száma, ami az árdiagramot tartalmazza és mindig elérhető: 0. Mindegyik indikátor segédablaknak szintén van egy száma. A segédablakokat egyszerűen fölülről lefelé számozzuk: az fő ablak alatti indikátor segédablak száma 1, az alatta lévőé 2, a következőé 3, stb ábra a segédablakok elhelyezkedése a szimbólum ablakban. A segédablakok számát könnyen meghatározhatjuk a következő függvénnyel: int WindowsTotal() A függvény visszaküldi az indikátor segédablakok összegét a főablakot is beleértve. A legnagyobb sorszámú (a legalsó) segédablak sorszám 1-el kisebb a szimbólumablakban lévő segédablakok összegénél (a fő ablakot is beleértve, aminek a sorsszáma 0). Ha a 140. ábra esetében valamelyik alkalmazásból végrehajtjuk WindowsTotal() függvényt, a visszaadott érték egyenlő lesz 3-mal, míg a legnagyobb sorszámú (a legalsó) segédablak sorszáma 2 lesz. A fent ismertetett számozási sorrend megmarad, ha egy új indikátor segédablakot nyitunk vagy egy létező segédablakot törlünk a szimbólumablakból. Ha hozzáadunk egy új segédablakot az minden más segédablak alatt fog megjelenni és sorszáma 1-el több a fölötte lévő utolsó ablak sorszámánál. Ha törölünk egy segédablakot a szimbólumablakból, minden ez alatti segédablak automatikusan újra fog számozódni - mindegyikük száma 1-el kisebb lesz. Az MQL4-ben lehetséges grafikus objektumokat létrehozni (és tulajdonságaikat megváltoztatni) bármelyik létező segédablakban. Az ObjectCreate() függvény 'window' paramétere lehetőséget nyújt a szimbólumablak azon segédablakának a meghatározására ahová az objektumot el akarjuk helyezni. A segédablak sorszámát meghatározhatjuk a következő függvénnyel: int WindowFind(string name) A függvény visszaküldi annak a segédablaknak a számát, ahol a 'name' paraméterben megadott indikátort megtalálta. Ha nem talált ilyen ablakot, akkor -1-et küld vissza. Az egyéni indikátor az init() futtása alatt saját magát nem találja meg (0-t fog visszaküldeni a WindowFind()). Paraméterek: name - az indikátor rövid neve

280 Egy szimbólumablakban levő segédablakok száma bármelyik pillanatban változhat, ha a felhasználó töröl egy indikátort. Ezért egy olyan alkalmazás algoritmusának, ami támogatja a grafikus objektumok ellenőrzését, folytonosan követnie kell az ablakok számát, amikben az indikátorokat futtatjuk. 34. feladat: Grafikus objektumok segítségével mutass be üzeneteket a két indikátor állapotáról. Ha a megfelelő indikátort csatoljuk a szimbólumablakhoz, az üzenet jelenjen meg az indikátorablakkal. Egyébként pedig a fő ablakban. A feladat megoldásához válasszunk az RSI és Momentum indikátorokat. Az Expert Advisor általános algoritmusát ennek megfelelően építjük fel. Az init() függvényben megadjuk azokat a szövegeket, amelyeket majd az indikátoroktól függően fogunk látni a képernyőn, ezeket a számításokat csak egyszer hajtjuk végre a programban. A start() függvényben neked kell kiválasztanod az indikátor üzenetet, meg határozni a segédablakok elérhetőségét és a számát, azután a helyzetnek megfelelően meg jeleníteni a megfelelő üzenetet a megfelelő segédablakban. A deinit() függvény végrehajtásakor törölni kell minden grafikus objektumot, amit a program létrehozott. Lent látható a charts.mq4 EA, amely vezérli a grafikus objektumokat a szimbólumablakban és a segédablakban. // // charts.mq4 // The code should be used for educational purpose only. // int Win_Mom_old=0, // Old number of subwindow Moment. Win_RSI_old=0; // Old number of subwindow RSI color Color[5]; // Declaration of the color array string Text[5]; // Declaration of the string array // int init() // Special function init() Win_RSI_old=0; // Technical moment Win_Mom_old=0; // Technical moment Text[0]= "RSI(14) is below 30. Buy"; // Texts for situations RSI Text[1]= "RSI(14) is above 70. Sell"; // Texts for situations RSI Text[2]= "RSI(14) is between 30 and 70"; // Texts for situations RSI Text[3]= "Momentum(14) is growing"; // Texts for situations Momentum Text[4]= "Momentum(14) is sinking"; // Texts for situations Momentum Color[0]= DeepSkyBlue; // Object color for.. Color[1]= LightPink; //.. different situations.. Color[2]= Orange; //.. of the indicator RSI Color[3]= Color[0]; // The same colors for Momentum Color[4]= Color[1]; // The same colors for Momentum Create_RSI(0); // Creation of the first object Create_Mom(0); // Creation of the second object Main(); // Call to user-defined function return; // Exit init() // int start() // Special function 'start' Main(); // Call to the user-defined function return; // Exit start() // int deinit() // Special function deinit() ObjectDelete("Obj_RSI"); // Deletion of the object ObjectDelete("Obj_Mom"); // Deletion of the object return; // Exit deinit()

281 // int Main() // User-defined function int // Integer variables Win_RSI_new=0, // New number of the subwindow RSI Win_Mom_new=0, // New number of the subwindow Moment. Ind_RSI, Ind_Mom; // Indexes for situations double // Real variables RSI, // Value of RSI on bar 0 Mom_0, Mom_1; // Value of Mom. on bars 0 and 1 // RSI=iRSI(NULL,0,14,PRICE_CLOSE,0); // RSI(14) on zero bar Ind_RSI=2; // RSI between levels 30 and 70 if(rsi < 30)Ind_RSI=0; // RSI at the bottom. To buy if(rsi > 70)Ind_RSI=1; // RSI on the top. To sell // Win_RSI_new=WindowFind("RSI(14)"); // Window number of indicator RSI if(win_rsi_new==-1) Win_RSI_new=0; // If there is no ind., then the main window if(win_rsi_new!=win_rsi_old) // Deleted or placed.. //.. window of indicator RSI ObjectDelete("Obj_RSI"); // Deletion of the object Create_RSI(Win_RSI_new); // Create an object in the desired window Win_RSI_old=Win_RSI_new; // Remember this window // Change the textual description: ObjectSetText("Obj_RSI",Text[Ind_RSI],10,"Arial",Color[Ind_RSI]); // Mom_0=iMomentum(NULL,0,14,PRICE_CLOSE,0); // Value on zero bar Mom_1=iMomentum(NULL,0,14,PRICE_CLOSE,1); // Value on the preceding bar if(mom_0 >=Mom_1)Ind_Mom=3; // Indicator line goes up if(mom_0 < Mom_1)Ind_Mom=4; // Indicator line goes down // Win_Mom_new=WindowFind("Momentum(14)"); // Window number of indicator Momen if(win_mom_new==-1) Win_Mom_new=0; // If there is no ind., then the main window if(win_mom_new!=win_mom_old) // Deleted or placed.. //.. the window of Momentum indicator ObjectDelete("Obj_Mom"); // Deletion of the object Create_Mom(Win_Mom_new); // Create an object in the desired window Win_Mom_old=Win_Mom_new; // Remember this window // Change the textual description: ObjectSetText("Obj_Mom",Text[Ind_Mom],10,"Arial",Color[Ind_Mom]); // WindowRedraw(); // Redrawing the image return; // Exit the user-defined function // int Create_RSI(int Win) // User-defined function //..of creation of an object ObjectCreate("Obj_RSI",OBJ_LABEL, Win, 0,0); // Creation of an object ObjectSet("Obj_RSI", OBJPROP_CORNER, 0); // Anchoring to an angle ObjectSet("Obj_RSI", OBJPROP_XDISTANCE, 3); // Coordinate X if (Win==0) ObjectSet("Obj_RSI",OBJPROP_YDISTANCE,20);// Coordinate Y else ObjectSet("Obj_RSI",OBJPROP_YDISTANCE,15);// Coordinate Y return; // Exit the user-defined function // int Create_Mom(int Win) // User-defined function //..creating an object ObjectCreate("Obj_Mom",OBJ_LABEL, Win, 0,0); // Creation of an object ObjectSet("Obj_Mom", OBJPROP_CORNER, 0); // Anchoring to an angle

282 ObjectSet("Obj_Mom", OBJPROP_XDISTANCE, 3); // Coordinate X if (Win==0) ObjectSet("Obj_Mom",OBJPROP_YDISTANCE, 5);// Coordinate Y else ObjectSet("Obj_Mom",OBJPROP_YDISTANCE,15);// Coordinate Y return; // Exit the user-defined function // Mielőtt a fenti kódot részletesen megvizsgálnánk, tekintsük át annak néhány jellemzőjét. Ha egy grafikus objektumot egyszer létrehoztunk (ebben az esetben a szövegeket) úgy tűnik, hogy az folyamatosan jelen van a képernyőn. A látható szövegről azt hihetnénk, hogy az pontosan visszatükrözi a jelenlegi helyzetet. A szöveg tartalmát meg kell változtatni a start() függvény minden végrehajtásánál minden ticknél. Azonban, amikor a különböző időkeretek között kapcsolgatjuk az ablakot, ahová az EA-t csatoltuk, a program végrehajtja a következő lépéseket: deinit(), init(), (várakozás a következő tickre), és start(). A start() legelső végrehajtásakor létrehozott objektum minden alkalommal, amikor egy másik időkeretbe kapcsolunk, a következő tick megérkezéséig el fog tűnni a képernyőről. Ez különösen akkor nagyon kellemetlen, ha gyakran kapcsolgatunk az időkeretek között. Egy megfelelően felépített programban a szükséges üzeneteket a képernyőn abban a pillanatban megjelenítjük, ahogy a programot a szimbólumablakhoz csatoltuk, vagy az időkeretet váltunk (nem várunk vele az új tick érkezésére). Ezért a szükséges kódrészeket végrehajtjuk minden ticknél a start() különleges függvény indításánál, és az init() különleges függvény végrehajtásakor is. Azért hogy ne kelljen megismételni ugyanazt a kódot mindkét különleges függvényben, a kódot különálló felhasználói függvénybe írjuk. Ezért tartalmazza az EA a Main() felhasználói függvényt. Ezt a függvényt hívjuk az inicializáláskor (2-3 blokk) és az EA további működése során minden ticknél (3-4 blokk). A programban (11-13 blokk), van még két további felhasználói függvény - Create_RSI() és Create_Mom(), melyek feladata az objektumok létrehozása és tulajdonságaik módosítása. Az init() függvény végrehajtásánál a szükséges objektumokat ezen függvények segítségével létrehozzuk. A Main() függvény hívása következtében létrejönnek az objektumok (a megfelelő színű szövegek megjelennek a kívánt ablakban). Vizsgáljuk meg a Main() függvényt (5-11 blokk) alaposan. A 6-7 blokkban az RSI indikátor értékét számoljuk ki. Attól függően, hogy az indikátor értéke 70 fölött, 30 alatt, vagy a két érték közötti tartományon belül van, az Ind_RSI változó különböző értéket fog kapni. Ezt az értéket tömbindexként fogjuk használni a Color[] és Text[] tömbökhöz ( 7-8 blokk) és ezzel megváltoztatjuk az Obj_RSI grafikus objektum (a felirat) tulajdonságait. 7-8.blokk: Meghatározzuk, hogy melyik ablakban helyezkedik el az RSI indikátor: Win_RSI_new = WindowFind("RSI(14)");// Window number of indicator RSI Az RSI(14) string értéket átadott paraméterként használjuk. Ez annak az indikátornak a rövid neve, aminek a helyét észlelni kell. Ebben az esetben az összes karakter, köztük a zárójelek és számjegyek alkotja a nevet. Ez azért szükséges, mert ugyanazon indikátor, különböző paraméterekkel lehet különböző ablakokhoz csatolva például: RSI(14), RSI(21) és RSI(34). Mindegyik segédablaknak, ahová ezeket az indikátorokat csatoltuk van saját száma. A könnyebb azoníthatóság érdekében kaptak a technikai indikátorok rövid nevet. A technikai indikátorok rövid neve látható a segédablakában a bal felső sarokban (egy egyéni indikátor rövid nevét létrehozhatja a programozó, ha használja az IndicatorShortName() függvényt). Ha a keresett indikátor nincs a szimbólumablakba, a Win_RSI_new változó (a segédablak száma ahol az üzenetet be kellene mutatni) -1 lesz, vagyis nincs ilyen ablak. Ebben az esetben a program a grafikus objektumot (az üzenetet) a 0 sorszámú ablakban jeleníti meg, vagyis a főablakban: if(win_rsi_new == -1) Win_RSI_new=0;// If there is no ind., then the main window A program működése alatt a felhasználó törölhet egy létező indikátort, vagy helyezhet el újakat. Hogy a program figyelemmel tudja követni a változásokat, használjuk a Win_RSI_old és Win_Mom_old globális változókat. Mindkét változó értéke annak a segédablaknak a száma, ahol az objektumot korábban létrehoztuk. Ha a Win_RSI_new és Win_RSI_old változók értékei nem esnek egybe, ez azt jelenti, hogy indikátorablakot csatoltunk vagy töröltünk (az előző tick óta). Mindkét esetben a korábban megalkotott objektumot törölni kell, és egy újat kell létrehozni a kívánt ablakban:

283 ObjectDelete("Obj_RSI"); // Deletion of the object Create_RSI(Win_RSI_new); // Create an object in the desired window Miután az objektumot létrehoztuk a Win_RSI_new paraméternek megfelelő ablakban, a Win_RSI_old változónak is ezt az értéket adjuk, vagyis a program emlékszik az ablak számára, amiben a grafikus objektumot létrehoztuk: Win_RSI_old = Win_RSI_new; // Remember this window Ha a Win_RSI_new és Win_RSI_old változók értékei azonosak, ez azt jelenti, hogy elegendő a szövegre vonatkozó paramétereket beállítani (és a paraméterek módosítását az objektumok jelenlegi helyén végrehajtani). Hasonlóan, mint az új objektumok létrehozásakor: ObjectSetText("Obj_RSI",Text[Ind_RSI],10,"Arial",Color[Ind_RSI]); Ugyanilyen számításokat végzünk a másik segédablakra a Momentum indikátorral (8-10 blokk). A Main() függvény végén, minden elérhető grafikus objektumot újrarajzolunk WindowRedraw() függvény végrehajtásával. Könnyű észrevenni, hogy a segédablakokban lévő grafikus objektumokat kezelő program globális változókat használ (használhatna statikus változókat is). Ilyenkor, a program kódolása közben fokozattan kell figyelni arra, hogy milyen értékeket vehetnek fel a különböző helyzetekben a globális változók, és, hogy ez milyen eredménnyel végződhet. A programban, amit fent láthatunk a globális változókat nullázzuk az init() függvényben : Win_RSI_old = 0; // Technical moment Win_Mom_old = 0; // Technical moment Ezek a sorok azért vannak a programban, mert a globális változók csak akkor veszítik el az értékeiket, ha a felhasználó leállította az alkalmazási program végrehajtását a szimbólumablakban. Azonban, ha a felhasználó külső változókat állít be vagy időkeretet vált, a program keresztülmegy a deinicializáción és utána rögtön az inicializáláson, de a globális változók értékei megmaradnak. Figyeljük meg, hogy mi történne, ha ezek a sorok hiányoznának a programból! Tegyük fel, hogy, amikor a felhasználó időkeretet vált mindkét indikátor a segédablakban van, az 1.-ben és a 2.-ban. A szóban forgó példában, mikor a program a deinicializációt végrehajtja, a grafikus objektumok törlődnek. Az init() különleges függvény végrehajtásánál, az objektumokat a nulla ablakban hozzuk létre. A Main() függvény végrehajtásánál a 7-8 és 9-10 blokkokban, a program összehasonlítja azon ablak számát, amiben az objektumot el kell helyezni, és azon ablak számát amiben az objektum az előző ticknél volt. Valójában, a tárgyak a nulla ablakban vannak, de a globális változók értékei más eredményt fognak mutatni, értékük 1 és 2 lesz. Végül a grafikus objektumok addig fognak maradni a fő ablakban, amíg a felhasználó le nem választja a megfelelő indikátorokat. Azért hogy meg akadályozzuk ezeket a nemkívánatos fejleményeket, a program elvégzi a globális változók nullázását az init() függvény végrehajtásánál. Így ezeknek a változóknak az értékei a valóságos helyzetet tükrözik. A charts.mq4 EA végrehajtása után az ablakok és grafikus objektumok következő kombinációit láthatjuk:

284 141. ábra. A grafikus objektumok megjelenítése egy szimbólumablakban és segédablakaiban. Ha mindkét indikátor a szimbólumablakban van, a megfelelő grafikus objektumok a saját segédablakaikban fognak meg jelenni. Ha ezek az indikátorok nincsenek csatolva, akkor mindkét objektumot a fő ablakban fogja létre hozni a program. Bármelyik indikátor hozzáadása vagy törlése, amit a programban szerepeltetünk, azzal fog végződni, hogy a grafikus objektum a megfelelő ablakba fog vándorolni. Egyéb indikátorok hozzáadása vagy törlése a szimbólumablakból nem fog előidézni semmilyen változást. Észre kell venni, hogy azt az esetet, amikor a felhasználó töröl egy grafikus objektumot, nem dolgozzuk föl ebben a programban. Egy programnak, amit a gyakorlati kereskedelmedben használnak, tartalmaznia kell az ilyen helyzet elemzését az azt követő helyreállítást (lásd a 33. feladat megoldását). A chartokkal kapcsolatos függvények Függvény HideTestIndicators Period RefreshRates Symbol WindowBarsPerChart WindowExpertName WindowFind WindowFirstVisibleBar Összefoglaló információ A függvény egy flaggel megjelöli az Expert Advisor által hívott, a teszt alatt elrejtendő indikátorokat. A tesztelő ábra megnyitásnál a megjelölt indikátorok nem fognak megjelenni a tesztgrafikonban. Mindegyik indikátorhívás előtt először ki kell tenni ezt a flaget (csak azok az indikátorok láthatók a tesztgrafikonban, amelyeket a teszt alatt közvetlenül hívtak az EA-ból). Visszaküldi az aktuális chart időkeretének értékét percekben. Az előre definiált változók és tömbök adatait frissíti. Ezt a függvényt használjuk akkor, amikor egy EA vagy egy script sokáig számolt és frissített adatokra van szüksége. Visszaküldi a TRUE-t, ha az adat frissítés sikerrel járt. Különben FALSE-t küld vissza. A régebbi adatok csak akkor maradnak érvényesek, ha egyeznek az ügyfélterminál aktuális adataival. Visszaküld az aktuális szimbólum nevét. A függvény visszaküldi az aktuális ablakban elérhető bárok számát. Visszaküldi a futó EA, script, egyéni indikátor vagy library nevét, attól az MQL4 programtól függően, ahonnan ezt a függvényt hívjuk. Visszaküldi annak a segédablaknak a számát, ahol a megnevezett indikátor, található, ha azt megtalálta. Ha nem találta meg -1-et küld vissza. A WindowFind() -1-et küld vissza akkor is ha az egyéni indikátor az inicializálás (init()) alatt kutat maga után. A függvény visszaküldi az aktuális chart első látható bárjának a számát. Figyelembe kell venni, hogy a bárokat fordított sorrendben számozzuk a legutolsótól visszafelé. Az aktuális bárnak, ami a legutolsó a sorban az indexe 0. A legrégebbi bárnak indexe Bars- 1. Ha az első látható bárnak a száma több mint 2-vel kisebb, mint a lehetséges bárok száma, ez azt jelenti, hogy az ablak nincs tele bárokkal és van egy üres tér a baloldalon

285 WindowHandle WindowIsVisible WindowOnDropped WindowPriceMax Visszaküldi azt az ablak azonosítót, ami tartalmazza az adott paraméterű chartot. Ha adott szimbólummal és időkerettel nincs nyitott chart 0-t küld vissza. TRUE értéket küld vissza, ha az adott segédablak látható. Különben FALSE-t küld vissza. A segédablak láthatóságát/elrejtését az indikátor tulajdonságainak beállítása határozza meg. Visszaküldi annak az ablaknak az indexét, amiben az EA-t, scriptet, vagy egyéni indikátort leejtették. Ez az érték csak akkor lesz igaz, ha az EA-t, indikátort vagy scriptet az egér húzásával csatoljuk az ablakhoz, ('drag and drop' módszer). Ha ezt a függvényt az egyéni indikátor inicializálásakor használjuk (az init() függvényből hívjuk), a hívó indikátor indexét nem tudja meghatározni. A visszaküldött index annak az ablaknak a száma (0 indexe van a főablaknak, az indikátor segédablakok számozása mindig 1-gyel kezdődik), amiben az egyéni indikátor dolgozik. Inicializálás alatt egy egyéni indikátor létre fogja hozni a saját új segédablakát, és ennek a sorszáma különbözni fog annak az ablaknak a számától, amiben az indikátort valójában leejtettük. Visszaküldi az aktuális ábra adott segédablaka függőleges skálájának a maximális értékét (0 a fő ábraablak,az indikátor segédablakok számozása pedig 1-gyel kezdődik). Ha híváskor az ablak indexet elhagyjuk, akkor a főablak árskálájának a maximális értékét fogja visszaküldeni. WindowPriceMin Visszaküldi az aktuális ábra adott segédablaka függőleges skálájának a minimális értékét (0 a fő ábraablak, az indikátor segédablakok számozása pedig 1-gyel kezdődik). Ha híváskor az ablak indexet elhagyjuk, akkor a főablak árskálájának a minimális értékét fogja visszaküldeni. WindowPriceOnDropped Visszaküldi az árértéket arról a pontról, 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ér húzásával csatoltuk, ('drag and drop' technológia). Ezt az értéket nem határozzák meg az egyéni indikátorok. WindowRedraw WindowScreenShot Erőszakosan újrarajzolja az aktuális ábrát. A függvényt általában azután használjuk, miután az objektum tulajdonságokat megváltoztattuk. Menti az aktuális képernyőképet egy GIF fájlban. Ha képtelen menteni FALSE-t küld vissza. WindowTimeOnDropped Visszaküldi az időértéket arról a pontról, 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ér húzásával csatoltuk ('drag and drop' technológia). Ezt az értéket nem határozzák meg az egyéni indikátorok. WindowsTotal WindowXOnDropped WindowYOnDropped A függvény visszaküldi a chart valamennyi indikátorablakának számát a fő ablakot is beleértve. Visszaküldi annak a pontnak az X koordinátáját pixelekben, 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ér húzásával csatoltuk, ('drag and drop' technológia). Visszaküldi annak a pontnak az Y koordinátáját pixelekben, 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ér húzásával csatoltuk, ('drag and drop' technológia)

286 String függvények A string értékekkel végzett leggyakoribb műveletet az összeadást (összefűzést) megtárgyaltuk az Operations and Expressions (Problem 3) részben. Néhány esetben szükség van a string értékekkel végrehajtott egyéb műveletekre is. Az MQL4-ben van néhány függvény a string típusú változókkal végrehajtott műveletekre. Vizsgáljuk meg néhányuk használatát a lenti példán keresztül! 35. feladat: Színezd az utolsót 100 bár gyertyáját a következők szerint: a fekete gyertyákat pirosra, fehér gyertyákat kékre. Egy gyertyát két vonallal lehet kiszínezni: egy vékony vonallal meg kell rajzolni a gyertya árnyékát, egy vastag vonallal pedig ki kell tölteni a gyertyatestet. Ebben az esetben, nem tudjuk használni egy egyéni indikátor vonalait sem, mert a rajzolt vonalaknak függőlegeseknek kell lenniük, vagyis e vonalak felépítéséhez két árkoordinátát kell meghatározni (ugyanazzal az idő koordinátával), azonban az indikátorvonalak esetén minden bárhoz csak egy árérték tartozhat. Tehát a probléma megoldása az, hogy különálló függőleges vonalakat rajzolunk az OBJ_TREND függvénnyel, meghatározott koordinátákkal, vonalstílussal és színekkel, (lásd: Grafikus objektumok). Ebben az esetben egy EA alkalmazási programot használunk, de általában az algoritmust megvalósíthatjuk egy egyéni indikátorban is. Az egész algoritmus egyszerű. Az ábrát egyből ki kell színezni, mikor az EA-t csatoljuk a szimbólumablakhoz (az init() végrehajtása alatt). A programnak a grafikus objektumok helyzetében bekövetkező minden változást követnie kell (a felhasználó véletlenül törölheti vagy elmozdíthatja őket) minden tick érkezésekor, ha szükséges vissza kell állítani az objektumokat. Minden objektumot, amit a program létrehozott, törölni kell, amint a programot eltávolítjuk (deinit()). A felhasználó egyéb objektumokat is létrehozhat a szimbólumablakban amíg ez az EA dolgozik, regressziós csatornát, Fibo szinteket, támaszvonalakat, stb. Ez az algoritmus lehetővé teszi, hogy megkülönböztessünk a felhasználó által létrehozott és a program által létrehozott objektumokat. Ez különösen a program befejezésekor fontos: csak a program által megalkotott objektumokat szabad eltávolítani, a felhasználó által létrehozott objektumoknak változatlanoknak kell maradniuk. A grafikus objektumoknak vannak olyan tulajdonságaik, amik megegyezhetnek más objektumok tulajdonságaival. Minden objektumot konkrétan azonosíthatunk az egyedi azonosító nevével (ugyanazon név többszörös használata tilos). Az objektumnak ajánlott olyan nevet választani, hogy az hasznos információt tartalmazzon, amely név jelzi az objektum helyét és tulajdonságait. Például egy objektumnév tartalmazhat egy olyan prefixumot, ami megkülönböztet egy program által létrehozott objektumot más objektumoktól. Ebben az esetben ez a Paint_. Azon kívül meg kell különböztetni a felhasználó által létrehozott objektumokat az egyéb objektumtól. Az egyszerű sorszámozás (Paint_1, Paint_2) nem használható. Ha ezt a módszert használjuk az objektumok számozására, akkor nem állapíthatjuk meg belőle pl. azt, hogy a Paint_73 objektumot melyik báron kell megjeleníteni. Arra a bárra, amelyen most a Paint_73 indexű objektum, az új bár érkezésekor a Paint_74 objektum kerül, majd a következő új bár kezdetekor a Paint_75, stb. Ebben az esetben törölni kellene, és újra kellene számolni minden objektumot minden egyes új bár kezdetekor. Ez a megoldás (bár ez lehetséges) nyilvánvalóan nagyon durva és erőforrás-pazarló. Minden objektumot a bár nyitás ideje alapján nevezünk el, ami megegyezik az objektum időkoordinátájával. Azon kívül minden bárra két vonalat kell rajzolni, egy vékonyat és egy vastagot. Legkényelmesebb a nevek követésére, ha az objektumokat a program nevezi el: Az objektum neve = Paint_2_ :40, ahol: Paint_ - előtag, ami jelzi, hogy az objektumot a program hozta létre; 2_ - a rajzolandó objektumazonosító száma, (értéke 1vagy 2 lehet); :40 - időkoordináta, ami egyértelműen jelzi a bárt, amelyre az objektumot rajzolni kell. A Paint_ és a 2_ a Prefix illetve Nom_Lin változók értékei. Az időkoordinátákat a bárok nyitási idejének string típusú értékre konvertálással kapjuk a következő függvénnyel:

287 TimeToStr() függvény string TimeToStr(datetime value, int mode=time_date TIME_MINUTES) A függvény átalakítja az óta másodpercekben eltelt időt (datetime típust) a meghatározott dátum/idő formátumra (string típusra). Paraméterek: value - a másodpercekben eltelt idő január 1. 00:00 óta; mode a kimenő adat formátuma. Egy egyszerű vagy összetett flag: TIME_DATE - a kimenő adat formátuma yyyy.mm.dd; TIME_MINUTES - a kimenő adat formátuma hh:mi; TIME_SECONDS - a kimenő adat formátuma hh:mi:ss. Nézzük, hogy a strings.mq4 EA hogyan kezeli gyertyák színezését és hogyan használja a TineToStr() függvényt ebben a programban: // // strings.mq4 // The code should be used for educational purpose only. // extern int Quant_Bars=100; // Number of bars datetime Time_On; string Prefix ="Paint_"; // int init() // Spec. function init() int Ind_Bar; // Bar index Time_On=Time [Quant_Bars]; // Time of first coloring for(ind_bar=quant_bars-1; Ind_Bar>=0; Ind_Bar--)// Bars cycle Create(Ind_Bar,1); // Draw a thin line Create(Ind_Bar,2); // Draw a thick line WindowRedraw(); // Image redrawing return; // Exit init() // int start() // Spec. function start() datetime T1, T2; // 1 and 2 time coordinates int Error,Ind_Bar; // Error code and bar index double P1, P2; // 1 and 2 price coordinates color Col; // Color of created object // for(int Line=1; Line<=2; Line++) // Line type cycle string Nom_Lin =Line + "_"; // String with the line number // string Nom_Lin = DoubleToStr(Line,0)+"_";// Can be so for(ind_bar=0; ;Ind_Bar++) // Bar cycle // datetime T_Bar= Time[Ind_Bar];// Bar opening time if (T_Bar < Time_On) break; // Don't color out of borders string Str_Time=TimeToStr(T_Bar); // Time string string His_Name=Prefix+Nom_Lin+Str_Time;// Object name // T1=ObjectGet(His_Name,OBJPROP_TIME1);// t1 coord. query Error=GetLastError(); // Error code receiving if (Error==4202) // If there is no object :( Create(Ind_Bar,Line); // Object creating function call

288 continue; // To the next iteration // T2 =ObjectGet(His_Name,OBJPROP_TIME2); // t2 coord. query P1 =ObjectGet(His_Name,OBJPROP_PRICE1);// p1 coord. query P2 =ObjectGet(His_Name,OBJPROP_PRICE2);// p2 coord. query Col=ObjectGet(His_Name,OBJPROP_COLOR); // Color query if(t1!=t_bar T2!=T_Bar // Incorrect coord. or color: (Line==1 && (P1!=High[Ind_Bar] P2!= Low[Ind_Bar])) (Line==2 && (P1!=Open[Ind_Bar] P2!=Close[Ind_Bar])) (Open[Ind_Bar] Close[Ind_Bar] && Col!=Red) (Open[Ind_Bar]==Close[Ind_Bar] && Col!=Green) ) ObjectDelete(His_Name); // Delete object Create(Ind_Bar,Line); // Create correct object // WindowRedraw(); // Image redrawing return; // Exit start() // int deinit() // Spec. function deinit() string Name_Del[1]; // Array declaring int Quant_Del=0; // Number of objects to be deleted int Quant_Objects=ObjectsTotal(); // Total number of all objects ArrayResize(Name_Del,Quant_Objects);// Necessary array size for(int k=0; k<=quant_del; i++) // Delete objects with names.. ObjectDelete(Name_Del[i]); //.. that array contains return; // Exit deinit() // int Create(int Ind_Bar, int Line) // User-defined function.. //..of objects creation color Color; // Object color datetime T_Bar=Time [Ind_Bar]; // Bar opening time double O_Bar=Open [Ind_Bar]; // Bar open price double C_Bar=Close[Ind_Bar]; // Bar close price double H_Bar=High [Ind_Bar]; // Bar maximum price double L_Bar=Low [Ind_Bar]; // Bar minimum price string Nom_Lin =Line + "_"; // String - line number // string Nom_Lin = DoubleToStr(Line,0)+"_";// Can be so string Str_Time=TimeToStr(T_Bar); // String - open time. string His_Name=Prefix+Nom_Lin+Str_Time;// Name of created object if (O_Bar < C_Bar) Color=Blue; // Choosing the color depending on.. if (O_Bar >C_Bar) Color=Red; //.. parameters of the bar if (O_Bar ==C_Bar) Color=Green; switch(line) // Thin or thick line case 1: // Thin line ObjectCreate(His_Name,OBJ_TREND,0,T_Bar,H_Bar,T_Bar,L_Bar); break; // Exit from switch case 2: // Thick line ObjectCreate(His_Name,OBJ_TREND,0,T_Bar,O_Bar,T_Bar,C_Bar); ObjectSet( His_Name, OBJPROP_WIDTH, 3);// Style ObjectSet( His_Name, OBJPROP_COLOR, Color); // Color ObjectSet( His_Name, OBJPROP_RAY, false); // Ray ObjectSetText(His_Name,"Object is created by the EA",10);// Description return; // Exit user-defined function // A grafikus objektumok létrehozására a Create() felhasználói függvényt (10-11 blokk) használjuk a programban. Az Ind_Bar változó jelzi annak a bárnak az indexét, amelyen az objektumot létre kell hozni, a Line az objektum száma (1 vagy 2), a paramétereit ebben a függvényben kapja

289 Az objektum neve három összetevőből áll: string His_Name = Prefix+Nom_Lin+Str_Time;// Name of created object A Prefix változó értékét a programozó a program fejrészében adja meg, és az nem változik meg a program végrehajtása alatt: string Prefix = "Paint_"; A Nom_Lin változó értéke számítások eredménye: string Nom_Lin = Line + "_"; // String - line number // string Nom_Lin = DoubleToStr(Line,0)+"_";// Can be so Itt az egész szám típusú változó típusa (a számítás alatt a kifejezés első része) átváltozik a magasabb prioritású string típussá. Végül a Nom_Lin a Lin változó értékétől függően 1_ vagy a 2_ értékeket kap. Az Str_Time változó értékének kiszámításához az adat átalakító TimeToStr() függvényt használjuk: string Str_Time = TimeToStr(T_Bar); // String - open time Vedd észre, hogy a TimeToStr() függvénynek alapértelmezett értéke van. Ebben az esetben a következő értékekre van szükség: yyyy.mm.dd hh, nem szükséges a másodpercek használata, mert a minimális időkeret 1 perc. Az objektum nevét alkotó Str_Time változó kiszámítására a következő módszert alkalmazhatjuk: string Str_Time = T_Bar; Ebben az esetben a Str_Time érték egyenlő az óta eltelt másodpercek számával. Azért, hogy a különbséget lássuk, írjunk és futtassunk egy olyan programot, ami a következő kódot tartalmazza: int init() string String_Time = TimeToStr(Time[0]); // Time in the format string String_Sec = Time[0]; // Number of seconds Alert("String_Time = ",String_Time," String_Sec = ",String_Sec); return; A következő üzenet (a nulla bár nyitási ideje) a programvégrehajtás során fog megjelenni a képernyőn: String_Time = :10 String_Sec = A strings.mq4 EA első alternatívája sokkal informatívabb, ezért ezt a változatot részesítjük előnyben (a két alternatíva az algoritmus szempontjából egyenértékű). A His_Name objektumot a Create() felhasználói függvény következő soraiban hozzuk létre. Ez tartalmazza a bár nyitás idejével kapcsolatos információt, és a bár jellemzőjétől függő "Line" paramétert, ami a vonal színét jellemzi. Szintén létrehozásra kerül az objektumok leírása minden egyes objektum esetén az"object is created by EA" tartalommal. A Create() függvényt a programban két helyről hívjuk: az init() különleges függvényből az objektumok legelső létrehozásakor, és a start() különleges függvényből az objektum újbóli létrehozására, ha azokat törölte vagy módosította a felhasználó. Az objektum neveket a start() függvényben (4-6) ugyanazon módszerrel alakítjuk, ki mint a program többi részében. A szóban forgó objektumok első koordinátáját a 6-7 blokkban határozzuk meg. Ha az objektum ekkor nem található meg, azt létre fogja hozni a Create() függvény. Ha az objektum létezik, a másik koordinátáját is meghatározza program és ellenőrzi, hogy az objektum paraméterei megfelelnek-e a bár paramétereinek (7-8 blokk). Az objektum törölve lesz es újra létre lesz hozva (ugyanazzal a névvel) a helyes tulajdonságokkal, ha bármilyen hibás illesztés van. Egy másik feladat, amit a deinit() függvény végrehajtása alatt meg kell oldani: csak azokat az objektumokat szükséges törölni, a szimbólumablakban lévő összes objektum közül, amelyeket ez a program hozott létre. Ennek a feladatnak a megoldása két lépésben történik: az első lépésben minden törlendő objektum nevét tároljuk a Name_Del[] tömbben, azután őket egy ciklusban törölni fogjuk. Az összes létező objektum

290 számának meghatározására (beleértve a program által, és a felhasználó által kézzel létrehozott objektumokat is) az ObjectsTotal() függvénnyel határozzuk meg: int Quant_Objects=ObjectsTotal(); // Total number of ALL objects A kiszínezendő bárok számát egy külső változóban határozza meg a felhasználó, ezért nem tudjuk előre, hogy hány objektumot kell törölni. Ezért azt a string tömböt, amelyik a törlendő objektumok nevét fogja tartalmazni 1 elemértékre, inicializáljuk. Később programozási módszerrel meghatározzuk az összes létező objektum számát és a tömb elemek számát erre az értékre növeljük. ArrayResize(Name_Del,Quant_Objects);// Necessary array size Hogy megkülönböztessük azokat az objektumokat, amelyeket az EA hozott létre a deinit() függvény tartalmaz egy olyan ciklust, ami elemzi minden objektum nevét. string Obj_Name = ObjectName(k); // Querying name of the object Az a sajátosság, ami megkülönbözteti az EA-nk által létrehozott objektumokat az összes többi objektumtól a névben szereplő Paint_ prefixum. Az objektum nevének elemzéséhez ki kell keresnünk az objektum nevének az elejét (ebben az esetben 6 karaktert) a string változóból és össze kell hasonlítani ezt az értéket a Prefix változóval. Ha azonosak, ezt az objektumot törölni kell. Ha nem, akkor nem kell törölni. StringSubstr() függvény string StringSubstr(string text, int start, int length=0) A függvény kiemeli a megadott pozíciónál kezdődő meghatározott hosszúságú részt a stringből, A függvény visszaküldi a kiemelt rész másolatát. Különben egy üres karakterláncot küld vissza. Paraméterek: text - az a string, amiből a string részt ki kell emelni; start - a kiemelendő rész kezdeti pozíciója. Értéke 0-tól StringLen(text)-1-ig terjedhet; length - a kiemelendő rész hossza. Ha ennek a paraméternek az értéke kisebb vagy egyenlő mint 0, vagy nincs megadva, a szövegrészt a megadott pozíciótól a string végéig kiemeljük. A szóban forgó példában a szövegrész (string részletet) a következőképpen nyerjük ki az objektum névből: string Head=StringSubstr(Obj_Name,0,6);// Extract first 6 symbols Ebben az esetben a nullától számított első 6 karaktert kinyerjük az Obj_Name string változóból. Jegyezd meg: minden index számozása (bárok, tömbök), a megbízások listája 0-val kezdődik, míg a számszerűsített számolás 1-gyel kezdődik. A kiemelt részt (egy string értéket) kapja a Head string változó. Ha az objektum nevet (és magát az objektumot) a szóban forgó EA hozta létre a kiemelt rész értéke Paint_ lesz. Ha egy másik értéket kapunk, akkor azt nem az EA hozta létre. Például a StdDev Channel név kiemelt része StdDev lesz, és a "Fibo 22800" ból kiemelt Fibo 2 lesz. A következő sorban a Head változó értékét összehasonlítjuk a Prefix változóval: if (Head == Prefix) // The object beginning.. //.. with Paint_ is found Ha ezek az értékek egyelőek egymásnak, akkor az objektum nevét hozzáadjuk a Name_Del[] tömbhöz a törlendő objektumok nevei közé. A következő ciklusban mindegyik objektum, aminek a neve a tömbben található törlésre kerül (meg kell jegyezni, hogy lehetetlen az első ciklus alatt törölni az objektumokat, mert ebben az esetben az objektumok mennyisége és számozása változni fog a ciklusban minden egyes objektum törlése után és ez néhány objektum kihagyásával végződik). A chart a strings.mq4 EA végrehajtása alatt a következő lesz:

291 142. ábra: Chart színezett grafikus objektumokkal (strings.mq4). A bárokat kiszínező vonalakon túl két másik olyan objektumunk is van, amit a felhasználó kézzel helyezett el, ahogy a 142. ábrán látható a regressziós csatorna és a Fibo szintek. Azokat az objektumokat, amiket az EA hozott létre, törölni fogjuk, amint az EA végrehajtása kész, azok az objektumok, amiket a felhasználóhozott létre, maradni fognak a szimbólumablakban. Ezt az eredményt a string függvények használatával értük el. Ezek a függvények lehetővé teszik a string értékek létrehozását, beleértve a grafikus objektumok neveit is. String függvények Függvény StringConcatenate StringFind StringGetChar StringLen StringSetChar StringSubstr StringTrimLeft StringTrimRight Rövid leírás Létrehoz egy stringet az adott paraméterekből és visszaküldi azt. A paraméterek bármilyen típusúak lehetnek. A paraméterek száma nem lehet több 64-nél. String-részlet kereső. Visszaküldi annak a pozíciónak a számát ahol a string részlet kezdődik, vagy -1-et, ha a string részletet nem találja. Visszaküldi annak a karakternek az értékét, ami a stringben a megadott pozíciónál található. Visszaküldi a karakterek számát a stringben. Kicseréli a stringben a meghatározott pozíciójú karaktert és visszaküldi a módosított stringet. A függvény kiemeli azt a meghatározott hosszúságú részt a stringből, ami a megadott pozíciónál kezdődik. A függvény visszaküldi a kiemelt rész másolatát. Ha nem sikerül egy üres karakterláncot küld vissza. A függvény kivágja a kocsi- visszaugrás-karaktereket, szóközöket és tabulálás szimbólumokat a string bal oldali részéről. A függvény visszaküldi a módosított string másolatát, ha lehetséges. Különben egy üres karakterláncot küld vissza. A függvény kivágja a kocsi- visszaugrás-karaktereket, szóközöket és tabulálás szimbólumokat a string jobb oldali részéről. A függvény visszaküldi a módosított string másolatát, ha lehetséges. Különben egy üres karakterláncot küld vissza

292 Adat átalakítási függvények Függvény CharToStr DoubleToStr NormalizeDouble StrToDouble StrToInteger StrToTime TimeToStr Összefoglaló információ Átalakítja az ASCII karakter kódját ASCII karakterré. A double típusú számot átalakítja stringé a meghatározott tizedes pontossággal. Kerekíti double típusú számot a megadott tizedes értékre. A StopLoss, TakeProfit és a függőben levő megbízások kívánt értékeit a megadott pontosság szerint kell normalizálni, a megkívánt pontosság a Digits változó értéke. A függvény átalakítja a string típusú számábrázolást double" típusú számmá (kétszeres-pontosságú formátum lebegőponttal). A függvény átalakítja a string típusú számábrázolást egész számmá, "int" típusú számmá (integer). A függvény átalakítja az időt és/vagy dátumot yyyy.mm.dd [hh:mi] formátumban tartalmazó string értéket datetime szám típusra (az óta eltelt másodpercek számára). Az óta eltelt, másodpercekben kifejezett időt átalakítja yyyy.mm.dd hh:mi string típusú formátumba

293 Dátum és idő A MetaTrader 4 online kereskedő rendszer két időforrás jeleit használja - a helyi időt (PC) és a szerveridőt. Local time helyi idő, a PC helyi idejét használjuk. Server time - szerveridő a szerver idejét használjuk. TimeLocal() függvény datetime TimeLocal() A függvény visszatérési értéke a PC-időnek az január a1.00:00 óta számolt értéke másodpercekben kifejezve. Megjegyzés: Teszt üzemmódban a helyi idő az utolsó ismert szerveridővel egyezik. Az ügyfélterminálban zajló események nagy többségét a szerveridőhöz viszonyítjuk. A tickek érkezését, új bárkezdetet, a megbízás megnyitás és zárási idejét a szerveridő alapján tartjuk nyilván. Az aktuális szerveridő értékét megkapjuk, ha használjuk a TimeCurrent() függvényt: TimeCurrent() függvény datetime TimeCurrent() A függvény visszaküldi a szerveridő utoljára ismert értékét (az utolsó tick idejét) az január 1-je 00:00 óta eltelt másodpercekben kifejezve. Az ügyfélterminál frissíti az utolsó szerveridőt (a többi előre meghatározott változó értékével együtt) a különleges függvények végrehajtása előtt. Mindegyik ticknek egyedi jellemzője a szerveridő értéke, azt megkaphatjuk a TimeCurrent() függvény használatával. A különleges függvények végrehajtása alatt ez az érték csak a RefreshRates() függvényhívás eredményeképpen változhat meg, és csak akkor, ha az információt a RefreshRates() függvény utolsó végrehajtása óta frissítették, vagyis az előre meghatározott változók a szervertől új értékeket kaptak. A Time[i] bár nyitási időpontjának nem kell egybeesnie egy új tick érkezésével. Bármilyen időkeret nyitás ideje az időkeret értékével mindig osztható. Az időkeretben megjelenő első tick megalakítja a bárt; ha egy időbeli kereten belül nem érkezi tick a bárt nem fog megalakulni az idő kereten belül. Például, a t0 (szerver) időpontban a terminálhoz érkező tick megalakít egy bárt a Time[i+2] időpontban (143. ábra). Az időkeret kezdete nem esik egybe a t0 pillanattal, bár ez véletlenül egyezhet is azzal. Azok a következő tickek, amik ugyanazon az időkereten belül érkeznek a terminálhoz, például a t1 és t2 a bár legmagasabb vagy legalacsonyabb ár paramétereit megváltoztathatják, de nem hatnak a bár nyitási idejére. A bárzárás időpontját nem tartjuk nyilván a MetaTrader 4 online kereskedő rendszerben (formálisan az utolsó tick érkezését az időkereten belül, vagy a következő időkeret nyitási idejét tekinthetjük egy bár zárási idejének 143. ábra) ábra. A bár kialakulásának folyamata a MetaTrader 4 online kereskedő platformban

294 A 143. ábrán láthatjuk, hogy előfordul az az eset, hogy bárok nem alakulnak meg egy időkeretben. Így, a t5 és a t6 pillanatban érkező tickek közötti időszakban egy új időkeret kezdődött és bezárult úgy, hogy új bár nem alakult abban az időszakban. Ebben az esetben a következő bár nyitási ideje, több mint egy egész időkeret értékével különbözik a szomszédos bár megnyitásának az idejétől, de ez a különbség az időkeret értékével mindig osztható. A bárok kialakulásának a folyamatát a timebars.mq4 EA-val tudjuk demonstrálni, ahol megfigyelhetjük a tick érkezésének és a bár megnyitásának időpontját: // // timebars.mq4 // The program is intended to be used as an example in MQL4 Tutorial. // int start() // Spec. function start() Alert("TimeCurrent=",TimeToStr(TimeCurrent(),TIME_SECONDS), " Time[0]=",TimeToStr(Time[0],TIME_SECONDS)); return; // Exit start() // A timebars.mq4 EA működésének eredményét láthatjuk a 144. ábrán. Nyilvánvaló, hogy az első tick az 1 perces időkeretben 14:29:12-kor érkezett, ugyanakkor az új bár nyitási ideje - 14:29:00. Vedd észre, hogy az üzenetdoboz jobb oldali oszlopa a szerveridőt mutatja, a bal oldali oszlop pedig a helyi időt. Fig Bar forming sequence in the online trading system MetaTrader 4. Abban az esetben, ha a tickek ritkán jönnek (például, az európai kereskedés vége és az ázsiai kereskedés kezdete közötti időszakban), a timebars.mq4 végrehajtása alatt megfigyelhetünk egy érdekes jelenséget: a szomszédos bárok nyitási ideje több mint egy perccel különbözhet egymástól (egy perces időkeretben). Ugyanakkor, a bárindexek megszakítás nélkül követik egymást. Különböző dealing centerekben levő szerverek szerverideje változhat. A kereskedés kezdeti és zárási idejét egyénileg állítják be mindegyik szerveren és ez eltérhet a kereskedési nap kezdetétől és végétől. Néhány dealing centeren például vasárnap 23:00-kor indul a szerveridő. Ezért lehetnek nem teljes napi bárok, amelyeknek a tartamuk gyakorlatilag egyenlő egy órával (145. ábra)

295 145. ábra. A különböző bártörténet a különböző dealing centereken. Az MQL4- ben a dátum és időfüggvények használata meglehetősen könnyű. Néhányuk átalakítja a szerveridőt vagy a helyi időt az január 1 00:00-óta eltelt másodpercek számából nappá, órává, perccé stb. Más függvények visszaküldenek egy olyan egész számot, ami megegyezik az aktuális, órával, nappal, perccel, stb. TimeSeconds (), TimeMinute(), TimeHour(), TimeDay(), TimeMonth(), TimeYear(), TimeDayOfWeek () és TimeDayOfYear() függvények Ez egy olyan függvényekből álló csoport, amelyek visszaküldik a másodpercekből számított a perc, óra, nap, hónap, év, a hét napja vagy az év napja értéket. Például: int TimeMinute(datetime time) A függvény visszaküldi a megadott idő percekben meghatározott értékét. Paraméterek: time - az január 1-je 00:00 óta eltelt másodpercek száma. int TimeDayOfWeek(datetime time) Ez a függvény visszaküldi a hét napját (0-vasárnap,1,2,3,4,5,6) ahová a megadott időpont esik. Paraméterek: time - az január 1-je 00:00 óta eltelt másodpercek száma. A szóban forgó függvényeket például bármelyik bár nyitási idejének elemzésére használhatjuk. A bigbars.mq4 EA megkeresi azt a legközelebbi bárt, amelynek a mérete nem kevesebb az előre beállított méretnél. // // bigbars.mq4 // The code should be used for educational purpose only. // extern int Quant_Pt=20; // Number of points

296 // int start() // Spec. function start() int H_L=0; // Height of the bar for(int i=0; H_L<Quant_Pt; i++) // Cycle for bars H_L=MathAbs(High[i]-Low[i])/Point;//Height of the bar if (H_L>=Quant_Pt) // if the high bar is not found int YY=TimeYear( Time[i]); // Year int MN=TimeMonth( Time[i]); // Month int DD=TimeDay( Time[i]); // Day int HH=TimeHour( Time[i]); // Hour int MM=TimeMinute(Time[i]); // Minute Comment("The last price movement more than ",Quant_Pt,//Message " pt happened ", DD,".",MN,".",YY," ",HH,":",MM);//output return; // Exit start() // A bigbars.mq4 EA megkeresi a legközelebbi bárt, amelynek a magassága (a minimum és maximum értéke közti különbség) több, vagy egyenlő azzal az értékkel, amit a Quant_Pt külső változóban megadtunk. A megtalált bár dátumát és idejét a Comment() függvénnyel jelenítjük meg a pénzügyi instrumentum ablakában. Seconds(), Minute(), Hour(), Day(), TimeMonth(), TimeYear(), DayOfWeek () és DayOfYear() függvények Ez az olyan függvényekből álló csoport, amelynek tagjai visszaküldik az aktuális másodpercet, percet, órát, napot, hónapot, évet, a hét napját és az év napját a legutolsó ismert szerveridőkor. Az utoljára ismert szerveridő az a szerveridő, ami megegyezik a programindítás pillanatával, (bármelyik különleges függvény indítása az ügyfélterminál által). A szerveridő nem változik meg a különleges függvények végrehajtása alatt. int Hour() Visszaküldi az utoljára ismert szerveridő aktuális óra értékét (0,1,2,..23). int DayOfYear() Visszaküldi az év aktuális napját (az 1. január - december 31.ig ; (6)), vagyis azt a napot amelyre az aktuális szerveridő esik. Tesztelés alatt a szerveridő értékét a program modellezi. A timeevents.mq4 EA elvégez néhány olyan feladatot, melyeknek során megfigyelhetjük a fent említett idő függvények használatát:( a kód nem jó!!) // // timeevents.mq4 // The code should be used for educational purpose only. // extern double Time_Cls=16.10; // Orders closing time bool Flag_Time=false; // Flag, there are no messages yet // int start() // Spec. start function int Cur_Hour=Hour(); // Server time in hours double Cur_Min =Minute(); // Server time in minutes double Cur_time=Cur_Hour + Cur_Min100; // Current time Alert(Cur_time); if (Cur_time>=Time_Cls) // If the time for the event has come Executor(); //.. then perform devised actions return; // Exit from start() // int Executor() // User-defined function if (Flag_Time==false) // If there are no messages yet

297 //.. then report (1 time) Alert("Important news time. Close orders."); Flag_Time=true; // Now the message has already appeared return; // Exit user-defined function // A start() különleges függvényben meghatározzuk órákban és percekben a szerveridőt (2-3 blokk). Ezek a programsorok: double Cur_time = Cur_Hour + Cur_Min100; // Current time képviseli az aktuális szerveridőt, amit a Cur_time változó értéke kifejez ki. A változókat az összehasonlító műveletekben kényelmesen használhatjuk: if (Cur_time >= Time_Cls) // If the time for the event has come Ha az aktuális idő több vagy egyenlő Time_Cls értékével, amit a felhasználó adhat meg, akkor megtörténik az Executor() felhasználói függvény hívása. Ebben a példában a felhasználói függvény az üzenetében kereskedési ajánlásokat ad. Általában ez a függvény bármilyen kódot tartalmazhat, például kereskedhet, e- maileket küldhet, grafikus objektumokat hozhat létre, stb. A dátum és idő függvények Function Day DayOfWeek DayOfYear Hour Minute Month Seconds Description Visszaküldi a hónap aktuális napját, vagyis a hónap utoljára ismert nappját a szerveridő alapján. Visszaküldi a hét napjának az indexét (vasárnap-0,1,2,3,4,5,6), az utoljára ismert szerveridő alapján. Visszaküldi az év aktuális napját (az első január 1, a 365(6). december 31.), az utoljára ismert szerveridő alapján. Visszaküldi az aktuális óra értékét (0,1,2,..23) a program kezdetekor (az érték nem változik meg a program (start függvény) végrehajtása alatt). Visszaküldi az aktuális perc érték a program kezdetekor (0,1,2,..59) (az érték nem változik meg a program végrehajtása alatt). Visszaküldi az aktuális hónap számát (1.-január,2,3,4,5,6,7,8,9,10,11,12) az utoljára ismert szerveridő alapján. Visszaküldi szerveridő aktuális percének a kezdete óta eltelt másodpercek számának a program kezdetekori értékét (az érték nem változik meg a program végrehajtása alatt). TimeCurrent Visszaküldi az utolsó ismert szerveridőt (az utolsó tick érkezési idejét), ami az január 1 00:00-óta eltelt másodpercek száma. TimeDay Visszaküldi, hogy a megadott idő a hónap melyik napjára esik (1-31). TimeDayOfWeek Visszaküldi, hogy a megadott idő a hét melyik napjára esik (0-vasárnap,1,2,3,4,5,6). TimeDayOfYear TimeHour TimeLocal TimeMinute TimeMonth TimeSeconds TimeYear Year Visszaküldi, hogy a megadott idő az év melyik napjára esik (1.január 1.,..,365(6). december 31.) Visszaküldi a megadott idő óra értékét. A függvény visszatérési értéke a PC-időnek az január a1.00:00 óta számolt értéke másodpercekben kifejezve. Visszaküldi a megadott idő perc értékét. Visszaküldi a megadott idő hónap értékét. (1-január,2,3,4,5,6,7,8,9,10,11,12). Visszaküldi a megadott időponttól eltelt másodpercek számát. Ez visszaküldi a megadott időpont év értékét. A visszaadott érték az es tartományon belül lehet. Visszaküldi az aktuális év értékét, vagyis az utoljára ismert szerveridő évét

298 Fájlműveletek Az MQL4-ben végrehajthatunk bizonyos fájlműveleteket. Szükségessé válhat, hogy információt írjunk egy fájlban vagy esetleg kiolvassunk egy fájlból. Egy fájlt arra a célra is használhatunk, hogy információt közvetítsen egy másik programnak. Ebben az esetben a fájlt létrehozhatja az alkalmazási program és az a program, amelyik az információt felhasználja. Például egy számla történelmét kiírhatjuk egy fájlba egy alkalmazási program végrehajtásával. Ezt a fájlt később megnyithatja egy másik program (például: az Excel azért, hogy grafikont rajzoljon). Más esetbe felmerül az igény arra, hogy egy alkalmazás információt kapjon a hírek érkezési idejéről. Egy végrehajtható program (például egy Expert Advisor) ki tudja olvasni ezt a korábban előkészített fájlból származó információt és grafikus meg tudja jeleníteni a képernyőn, vagy felhasználhatja kereskedelmi döntéseknél. Fájlnevek és mappák Egy munkafájl nevét az operációs rendszer követelményei szerint kell létrehozni. Minden fájlnév, amit az MQL4 használ, két részből áll: az állomány neve és az állomány kiterjesztése egy ponttal elválasztva, például: News.txt. Technikailag az állománynévnek nincs kapcsolata a fájltartalommal, úgyhogy az állománynevet és kiterjesztést a programozó önként megválaszthatja. Egy állománynevet általában úgy választunk meg, hogy az utaljon arra az információra, amit a fájl tartalmaz. A legtöbb program automatikusan elindul a felhasználó PC-jén, ha az egérgombbal a fájlra duplán rákattintunk. Az állomány-kiterjesztés szerint betöltődik egyik vagy másik program, ami megjeleníti a fájltartalmat. Ezért az állomány-kiterjesztést arra a programra tekintettel kell megválasztani (ha ez szükséges) amelyik program majd használni fogja ezt a fájt. A legtöbbször használt állománytípusok (a típust meghatározza a kiterjesztés) a következők: -. txt - szövegállomány, megnyitásához használható programok: Notepad, Word, FrontPage, stb.; -. csv az Excel táblázatot felépítő fájlformátum; -. htm a z ilyen fájlokat a böngészőkben nyithatjuk meg, Internet Explorer, Netscape Navigátor,, stb. Van három mappa (alkönyvtárakkal) ahol a munkafájlokat tároljuk: - Terminal_folder\Experts\History\current broker\ - az események naplózásának a mappája; - Terminal_folder\Experts\Files\ - általános használatra; - Terminal_folder\Tester\ Files\ - ide kerülnek a tesztelés során használt fájlok. Egy munkafájlt elmenthetünk e mappák valamelyikébe, vagy ezek valamelyik almappájába. Ha a mappa a mentés pillanatában nem létezik, akkor azt automatikusan létre fogja hozni az ügyfélterminál. Az ügyfélterminál fájlműveletei az egyéb mappákban lévő fájlokat nem befolyásolják. A fájlműveletek módjai Egy alkalmazás és egy munkafájl közötti kölcsönhatásnak több módja van. Általában egy fájlt ugyanabban az időben több program is megnyithat (egy PC-n belül vagy a hálózaton keresztül több PC). Ugyanakkor a működési környezet csak egy programnak adja meg a teljes hozzáférést a fájlhoz, egyszerre több program olvashatja a fájlt, de írási joga csak egy programnak van. Például, ha a My_text.doc-ot már kinyitotta egy szövegszerkesztő, és azután egy másik program is meg akarja nyitni akkor a fájl megnyitása előtt egy értesítés fog megjelenni:

299 146. ábra. Megjelenik egy dialógusdoboz, ha egy program megpróbál elérni egy olyan a fájlt, amit már korábban kinyitott egy másik program. Ez a technológia a garancia arra, hogy egy fájlt nem fog módosítani egyidejűleg két különböző program. Azért hogy egy alkalmazási program és egy fájl egymásra hasson, először meg kell nyitni azt a fájlt. A fájl megnyitásának módját a FileOpen() függvényben határozzuk meg. Egy alkalmazási program egy időben több fájlt is megnyithat. Azért, hogy a program egyértelműen megkülönböztesse egyik fájlt a másiktól, minden nyitott fájl kap egy állományleírót. File descriptor, állományleíró - a pillanatnyilag nyitott fájl egyedülálló azonosító száma. A FileOpen() függvény vissza fog küldeni egy egész számot (ez az érték a 'handle' változó), ha egy fájlt sikeresen megnyitott. Ez az érték az állományleíró. A legtöbb függvény, amely fájlműveleteket hajt végre, a formális paraméterei között használja az állományleírót is. FileOpen() függvény int FileOpen(string filename, int mode, int delimiter=';') A függvényírásra és/vagy olvasásra megnyit egy fájlt. A függvény visszaküldi az állományleírót, vagy kudarc -1-et. A megnyitandó fájlok a Terminal_folder\Experts \Files\ mappában vagy a Terminal_folder\Tester\Files\ mappában (EA tesztelés esetében) vagy ezek alkönyvtáraikban lehetnek. Paraméterek: filename - a fájl neve; mode - a fájlmegnyitás módja; a következő értékek lehetnek : FILE_BIN, FILE_CSV, FILE_READ, FILE_WRITE (vagy ezek kombinációi); delimiter - a csv-fájl elválasztójele. Ez alapból ';'. A fájlmegnyitás FILE_READ módja azt jelenti, hogy a fájlt csak olvasásra fogja használni a program. Ha a megnyitás ebben a módban nem sikerült, ez azt jelenti, hogy ezzel a névvel nincs fájl a megadott helyen. A FILE_WRITE módú megnyitásnak az a következménye, hogy a program egy írható fájlt fog megnyitni. Az ily módon megnyitott fájl egy nullahosszúságú fájl lesz. Még akkor is, ha a megnyitás előtt az ilyen nevű fájlnak volt tartalma ez az információ ki fogják törlődni. Az ilyen módú kísérlet sikertelen lesz, ha a fájlt korábban kinyitotta egy másik program (írás módban). Egy fájlt megnyithatunk FILE_READ FILE_WRITE módban is. Ekkor lehetőség van a fájl olvasására és írására is. Ezt a módot akkor használjuk, ha hozzá kell adni valamilyen információt ahhoz a fájlhoz, ami már tartalmaz egy másik információt. A fájlmegnyitás módjának megadása kötelező: FILE_READ vagy FILE_WRITE, vagy a kombinációjuk. A fájlmegnyitás FILE_BIN módja meghatározza, hogy a munkafájlt binárisként dolgozzuk fel. A fájlmegnyitás FILE_CSV módja a szövegként történő feldolgozást írja elő. A függvényben kötelező megadni a FILE_BIN vagy a FILE_CSV módot. FILE_BIN és FILE_CSV módok egyidejű használata tilos. A függvényben a FILE_READ, FILE_WRITE vagy a FILE_READ FILE_WRITE módokat kötelező kombinálni a FILE_BIN vagy a FILE_CSV móddal. Például: a FILE_CSV FILE_READ kombinációt arra használjuk, hogy kiolvassa az információt egy szöveges állományból, a FILE_BIN FILE_READ FILE_WRITE kombinációt pedig arra, hogy hozzáadjon egy bejegyzést egy bináris állományhoz

300 Egyszerre 32-nél több fájlt nem lehet megnyitni egy végrehajtható modulon belül (alkalmazási program, Expert Advisor). Az egyik modulban megnyitott fájlok leíróit nem adhatjuk át egy másik modulba. A fájlbejegyzések tartalma A fájlba történő bejegyzéseket térköz nélkül írjuk a fájlba, a fájlmegnyitás minden kombinációjánál. A FILE_BIN módban az információ bejegyzéseket változatlanul hozzáadjuk az alakítandó fájlhoz. Az információ fajtájától függően, amit a fájlba írunk (és attól a függvénytől függően, amivel ezt végrehajtjuk) a ("\r\n") sorvégzáró karakterkombinációt is használhatjuk a bejegyzés csoportok között. Az információbejegyzéseket a FILE_CSV módban az elválasztó karakterekkel (általában ';') szeparáljuk, és a bejegyzés csoportokat (a bejegyzés sorait) a sorvégzáró karaktercsoportokkal ("\r\n") választjuk el egymástól). File separator, elválasztót karakter - különleges szimbólum; a bejegyzésen belül elválasztja az információrészeket egymástól. A fájl elválasztó karaktert arra használják, hogy a csv fájlban elválassza az egyes információ bejegyzéseket. A közös elv a bejegyzések írásakor, hogy azokat bármilyen fájlba is írjuk, a karakterek között nem hagyunk térközt. Vagyis, a bejegyzés folytonos szimbólum sorozatból áll. A fájlokat többféle program is olvashatja (a megvalósítási szabályoktól függően) és különböző formában jeleníti meg a képernyőn. Például: itt van a File_1.csv fájl: int FileOpen(string filename, int mode, int delimiter=';') A File_1.csv fájlt a különböző programokban különböző alakban láthatjuk: 147. ábra. File_1 ábrázolása különböző programokban (File_1.csv). Ebben az esetben az \r\n szimbólumkombinációt mindkét program (Excel és Notepad) szövegformázó karakterekként értelmezte: a karaktereket az \r\n kombináció után a következő sorba írta, és magát az \r\n kombinációt nem jeleníti meg a szerkesztőablakban. Ugyanakkor az Excel táblázatkezelő a ";" szimbólumot oszlop elválasztóként értelmezte, vagyis az információk elválasztójaként. Figyeljük meg, hogy a ";" szimbólumot nem mutatja az Excel. A Jegyzettömb egy szövegszerkesztő. Ez a program nem teszik lehetővé az információ felosztását oszlopokba, ezért a ";" szimbólumot nem értelmeztek bejegyzés elválasztóként, hanem azt az információ részeként értelmezte, ezért azt is kiírta a képernyőre. A nevezett karaktereket (";" és \r\n) elválasztó karakterként használja az MQL

301 148. ábra. A bejegyzések variációja a fájlműveletek során. A 148. ábrán a különböző kiterjesztésű fájlokba írt információ szerkezetét láthatjuk. A legfelső sorban a csvfájlok láthatók, az alsó három sor pedig a bináris állományok szerkezete. Ezeket a fájlokat a rájuk jellemző speciális szabályok szerint hozza létre és írja a függvény. A bejegyzések a csv fájlban string értékek (string típusúak) elválasztó karakterrel vagy sorvég karakterekkel tagolva. Mindkét tagolási módszert a bejegyzett értékrész végeként értelmezik olvasás közben az MQL4 erre alkalmas beépített függvényei. A string értékek különböző hosszúságúak lehetnek és nem tudjuk, hogy hány karaktert tartalmaznak, ezért a kiolvasásukat a következő elválasztó karakterig végezzük. A fájlbejegyzések másik típusát a bináris állományok képviselik, a bináris állományokat elválasztó karakterek nélkül írjuk. Az ilyen bejegyzéseknek rögzített hosszuk van a különböző adat típusokhoz: 4 byte az "int", "bool", "datetime" és "color"típusokhoz, és 8 byte (vagy 4 byte, az írás paramétereitől függően) a "double" típusú adatokhoz. Ebben az esetben az elválasztó karakterekre nincs szüksége, mert az olvasást a beépített függvény az adattípusra jellemző bejegyzés hosszal hajtja végre. Az utolsó (a legalsó a 148. ábrán) bináris állomány string típusú adatokat tartalmaz amelyeket sorvég karakterekkel tagolunk. File pointer, fájlmutató az a hely a fájlban, ahol a következő érték olvasását kezdjük. A fájlmutatót kurzorként képzelhetjük el. A fájlmutató meghatározza a fájlban elfoglalt helyet. Az olvasás alatt a fájlmutató jobbra mozdul egy vagy több pozíciót. 36. Feladat: Olvassa ki a fontos hírekkel kapcsolatos információt egy fájlból, és jelenítse azt meg grafikus objektumokkal a charton (függőleges vonallal) a bejelentés időpontjának megfelelően. Legyen a Terminal_Folder\Experts\Files\ mappában lévő News.csv fájl tartalma a következő: 149. ábra. A News.csv. munkafájl tartalma. News.csv. Ebben az esetben a fájl öt eseménnyel kapcsolatos információt tartalmaz, ami különböző országokban fog történni különböző időpontokban. Mindegyik sor két bejegyzést tartalmaz. Az első bejegyzés az esemény bekövetkeztének idejét jelző string érték. A második bejegyzés az esemény szöveges leírása. A második bejegyzés első három szimbóluma tartalmazza a pénznemet (országot) amelyre az esemény hatással lehet

302 A feladat megoldása két részből áll. Először is ki kell olvasnunk az információt a munkafájlból azután a grafikus objektumok koordinátáiként kell használnunk a megkapott értékeket. Az információ olvasását a FileReadString() függvény hajtja végre. FileReadString() függvény string FileReadString(int handle, int length=0) A függvény kiolvassa a fájlból az információt az aktuális pozícióból. Ez ugyanúgy történik a CSV-nék és a binárisnál is. A sort addig fogja olvasni, amíg az elválasztójellel nem találkozik a szövegállományban. A bináris állományokból meghatározott számú szimbólumot fog olvasni. Ha a hiba információra vagyunk kíváncsiak, hívnunk kell a GetLastError() függvényt. Paraméterek: handle - az az állományleíró, amit visszaküld a FileOpen() függvény; length az olvasandó karakterek száma. A szükség hírekkel kapcsolatos információt csak egyszer kell feldolgozni a kereskedés kezdetén, tehát ebben az esetben a 36. feladat megoldásához egy scriptet kell használnunk. A timetablenews.mq4 script feladata az, hogy kiolvassa az információt a fájlból és ábrázolja azt grafikus objektumokkal a szimbólumablakban. // // timetablenews.mq4 // The code should be used for educational purpose only. // int start() // Spec. function start() // int Handle, // File descriptor Stl; // Style of vertical line string File_Name="News.csv", // Name of the file Obj_Name, // Name of the object Instr, // Name of the currency One,Two, // 1st and 2nd name of the instr. Text, // Text of event description Str_DtTm; // Date and time of the event (line) datetime Dat_DtTm; // Date and time of the event (date) color Col; // Color of the vertical line // Handle=FileOpen(File_Name,FILE_CSV FILE_READ,";");// File opening if(handle<0) // File opening fails if(getlasterror()==4103) // If the file does not exist,.. Alert("No file named ",File_Name);//.. inform trader else // If any other error occurs.. Alert("Error while opening file ",File_Name);//..this message PlaySound("Bzrrr.wav"); // Sound accompaniment return; // Exit start() // while(fileisending(handle)==false) // While the file pointer.. //..is not at the end of the file // Str_DtTm =FileReadString(Handle);// Date and time of the event (date) Text =FileReadString(Handle);// Text of event description if(fileisending(handle)==true) // File pointer is at the end break; // Exit reading and drawing // Dat_DtTm =StrToTime(Str_DtTm); // Transformation of data type Instr =StringSubstr(Text,0,3);// Extract first three symbols One=StringSubstr(Symbol(),0,3);// Extract first three symbols Two=StringSubstr(Symbol(),3,3);// Extract second three symbols Stl=STYLE_DOT; // For all - dotted line style Col=DarkOrange; // For all - this color if(instr==one Instr==Two) // And for the events of our

303 //.. symbol.. Stl=STYLE_SOLID; //.. this style.. Col=Red; //.. and this color of the vert. line // Obj_Name="News_Line "+Str_DtTm; // Name of the object ObjectCreate(Obj_Name,OBJ_VLINE,0,Dat_DtTm,0);// Create object.. ObjectSet(Obj_Name,OBJPROP_COLOR, Col); //..and its color,.. ObjectSet(Obj_Name,OBJPROP_STYLE, Stl); //..and style.. ObjectSetText(Obj_Name,Text,10); //..and description // FileClose( Handle ); // Close file PlaySound("bulk.wav"); // Sound accompaniment WindowRedraw(); // Redraw object return; // Exit start() // A változókat a 2-3 blokkban írjuk le. A fájl megnyitását és a megnyitási kísérlet eredményének az elemzését a 3-4 blokkban hajtjuk végre. A fájlt a FileOpen() függvény segítségével nyitjuk meg: Handle=FileOpen(File_Name,FILE_CSV FILE_READ,";");// File opening A fájl megnyitása nem mindig sikeres. Ez előfordul, ha a fájl a megadott névvel nem létezik. Amikor a fájlmegnyitás sikertelen (az állományleíró egy negatív szám) a szükséges szöveges üzenetet megjelenítjük a felhasználónak és a start() függvény végrehajtása véget ér. A fájl sikeres megnyitása esetén a vezérlést megkapja a "while" ciklusoperátor (4-8 blokk). A fájlból való adatok olvasása (5-6 blokk), az adatok átalakítása és az elemzése (6-7 blokk) és a koordinátákkal és paraméterekkel rendelkező grafikus objektum létrehozása a legutolsó kiolvasott információnak megfelelően történik (7-8 blokk) mindegyik ismétlésnél. A "while" ciklus végrehajtása addig folytatódik, amíg a fájlmutató el nem éri a fájl végét, vagyis információ nem marad a fájlmutató jobb oldalán. A FileIsEnding() függvény feladata, hogy elemezze a fájlmutató pozícióját. FileIsEnding() függvény bool FileIsEnding(int handle) A függvény visszaküldi a TRUE értéket, ha a fájlmutató a fájl végén van, különben FALSE-ot küld vissza. Hibainformációt, a GetLastError() függvény segítségével kapunk. A GetLastError() függvény az ERR_END_OF_FILE (4099) hibát fogja vissza küldeni abban az esetben, ha a fájl az olvasás alatt ér véget. Paraméterek: handle - állományleíró, amit a FileOpen() függvény küld vissza. A jelen megoldás (timetablenews.mq4) lehetővé teszi, hogy bármennyi bejegyzés lehet a News.csv fájlban. Az említett példában (149. ábra) a News.csv fájl öt bejegyzést tartalmaz, ami öt eseményhez tartozik (hírek). Általában a bejegyzések száma 0-tól ig terjedhet, az adott napon történő eseményektől függően. A bejegyzés olvasása a fájlból (abból amelyiket a "handle" változó kijelölt) az 5-6 blokkban történik: Str_DtTm =FileReadString(Handle);// Date and time of the event (date) Text =FileReadString(Handle);// Text of event description if(fileisending(handle)==true) // File pointer is at the end break; // Exit reading and drawing Az 5-6 blokk első és második soraiban addig olvassuk az információt a fájlból, amíg a legközelebbi elválasztóval nem találkozunk. A harmadik és negyedik sorban ellenőrizzük, hogy a fájlmutató a sor végén van-e. Ha nem, akkor a ciklusban a grafikus objektumokat ki fogjuk alakítani a két kiolvasott string érték szerint. Ha kezdetben tudnánk a bejegyzések számát, a harmadik és negyedik sorban lévő elemzés

304 végrehajtására nem lenne szükség. Ebben az esetben megadnánk az ismétlések számát a ciklusban (pl.: 5) és nem hajtanánk végre a bejegyzések számára vonatkozó ellenőrzést. Azonban a bejegyzések száma ebben az esetben ismeretlen. Ugyanakkor ebben a példában minden eseményt két érték ír le, amelyek a következő formátumú sort alkotják: érték; elválasztót karakter; érték; sorzáró karakter. Ebben az esetben feltételezzük, hogy ha van első bejegyzés (első érték a sorban) akkor a második is létezik; és ha nincs első bejegyzés, akkor a második sem létezik, ezért kijelezendő esemény nincs és nem szükséges a grafikus objektumot létrehozni. Ha az egyik vagy mindkét bejegyzés hiányzik, a fájlmutató a fájl végre ér (arra a helyre a fájlban, ahol a mutató jobb oldalán nincsenek adatok) az olvasási kísérlet során. A 3-4 blokkban végrehajtott ellenőrzés segít felfedezni ezt a tényt. Ha az ellenőrzés elmaradna (az 5-6 blokk utolsó két sora), akkor fölösleges objektumokat hozna létra a program. Csak a "while" ciklusoperátor feltételének teljesülése után kerül a vezérlés a 8-9 blokkba. Az adat, amit a fájlból kiolvastunk string típusú. Ahhoz, hogy a megkapott értékeket grafikus objektumok létrehozására használhassuk, át kell alakítani az adatokat a szükséges adattípusra. A 6-7 blokkban, az első (a következő sorból olvasott) értéket alakítjuk át datetime típusra és a későbbiekben ezt az értéket fogjuk használni a megfelel az eseményt jelző grafikus objektum koordinátájaként. A második string érték első három karakterét összehasonlítjuk a szimbólum nevében szereplő első és második deviza nevével. Ha van egybeesés, akkor azalapján a grafikus objektum megkapja a megfelelő paramétereket: a vonalstílus: folytonos és a szín: piros (7-8 blokk). Egyéb esetekben az objektumokat narancssárga pontozott vonallal ábrázoljuk. Megfigyelhetjük a script végrehajtásának az eredményét: 150. ábra. Grafikus objektumok a timetablenews.mq4 végrehajtása után a szimbólumablakban. Ezt a scriptet bármilyen szimbólumablakban végrehajthatjuk. Azonban minden ablakban folytonos piros vonallal ábrázolva jelennek meg azok az események, amik érintik az ablak devizáit, és pontozott narancssárga vonallal azok amelyek másik szimbólumok eseményeivel kapcsolatosak. Az objektumok leírásának megjelenítésére kapcsoljuk be az Objektum leírások megjelenítése opciót az ablak Tulajdonságok-ban (F8) => Általános fül. A korábban megnyitott fájlt a 8-9 blokkban a feladat megoldása után bezárjuk, mivel minden szükséges objektumot létrehoztunk. A fájlt a következő okok miatt kell bezárni: először azért, hogy ne terheljük fölöslegesen a PC-erőforrásokat, másodszor pedig azért, hogy lehetővé tegyük másik programoknak, hogy hozzáférjenek a fájlhoz írás módban. Ezért ajánlott bezárni a fájlt, amint minden információt kiolvastunk (vagy írtunk) és a használata már nem szükséges. A fájl befejezését a FileClose() függvény hajtja végre. FileClose() függvény void FileClose(int handle) A függvény bezár egy olyan fájlt, amit korábban a FileOpen() függvény nyitott meg. Paraméterek:

305 handle állományleíró, amit a FileOpen()függvény küldött vissza. Azért hogy a gyakorlatban használni tudjuk a timetablenews.mq4 scriptet valamilyen módszerrel létre kell hozni azt a fájlt, ami tartalmazza az adott időszak bejelentéseinek időpontját. Ezt a fájlt létrehozhatjuk bármilyen szövegszerkesztővel, azonban ebben az esetben fennáll a hiba lehetősége (néha egy elválasztó karaktert hibásan helyezünk el). A vizsgáljuk meg e fájl létrehozását egy MQL4 program segítségével. 37. feladat: Írjuk meg annak az EA-nak a kódját, ami létrehozza a fontos bejelentéseket tartalmazó fájlt. Általában egy EA-val létre lehetne hozni egy olyan fájt, ami korlátlan számú hír bejelentési időpontját tartalmazza. A bemutatandó createfile.mq4 programmal egy olyan munkafájlt hozhatunk létre, ami ötnél nem több esemény információját tartalmazhatja. // // createfile.mq4 // The code should be used for educational purpose only. // extern string Date_1=""; // :30 extern string Text_1=""; // CHF Construction licenses extern string Date_2=""; // :00 extern string Text_2=""; // GBP Refinance rate,2%,2.5% extern string Date_3=""; // :15 extern string Text_3=""; // EUR Meeting of G10 banks governors extern string Date_4=""; // :30 extern string Text_4=""; // USD USA unemployment rate extern string Date_5=""; // :30 extern string Text_5=""; // JPY Industrial production // int start() // Spec. function start() // int Handle, // File descriptor Qnt_Symb; // Number of recorded symbols string File_Name="News.csv"; // File name string Erray[5,2]; // Array for 5 news // Erray[0,0]=Date_1; // Fill the array with values Erray[0,1]=Text_1; Erray[1,0]=Date_2; Erray[1,1]=Text_2; Erray[2,0]=Date_3; Erray[2,1]=Text_3; Erray[3,0]=Date_4; Erray[3,1]=Text_4; Erray[4,0]=Date_5; Erray[4,1]=Text_5; // Handle=FileOpen(File_Name,FILE_CSV FILE_WRITE,";");//File opening if(handle==-1) // File opening fails Alert("An error while opening the file. ",// Error message "May be the file is busy by the other applictiom"); PlaySound("Bzrrr.wav"); // Sound accompaniment return; // Exir start() // for(int i=0; i<=4; i++) // Cycle throughout the array if(stringlen(erray[i,0])== 0 // If the value of the first or.. StringLen(Erray[i,1])== 0) //..second variable is not entered break; //.. then exit the cycle Qnt_Symb=FileWrite(Handle,Erray[i,0],Erray[i,1]);//Writing to the file if(qnt_symb < 0) // If failed

306 Alert("Error writing to the file",getlasterror());// Message PlaySound("Bzrrr.wav"); // Sound accompaniment FileClose( Handle ); // File closing return; // Exit start() // FileClose( Handle ); // File closing Alert("The ",File_Name," file created.");// Message PlaySound("Bulk.wav"); // Sound accompaniment return; // Exit start() // A kezdeti információkat a program elején (1-2 blokk) string típusú külső változókban helyezzük el. A többi változót a 3-4 blokkban hozzuk létre és írjuk le. A feldolgozás egyszerűbbé tételéért az adatokat az Erray[][] tömbbe helyezzük el. Minden eseményt (a híreket jellemező információ) a kétdimenziós tömb két eleme képvisel. A tömb méretét (a tömbben lévő elemek számát) a hírek száma határozza meg, ebben az esetben ez 5. Azért, hogy az adatbevitel során a hibás működést elkerüljük, az example_news.set fájlt létrehozó EA -t demó-számlán futtassuk, az EA által létrehozott fájlnak a Terminal_folder\presets mappába kell kerülnie. A fájlmegnyitás az 5-6 blokkban történik. Ha a művelet nem sikerül, akkor a start() függvény véget ér, és a felhasználó megkapja a megfelelő üzenetet. Ha a fájlt sikeresen megnyitottuk, akkor a vezérlés át fog kerülni a 6-7 blokkban lévő ciklusoperátorhoz. A bemenő adatok számát, az Erray tömb méretét és az ismétlések számát, ha szükséges megnövelhetjük. Mindegyik ismétlésnél ellenőrizzük, hogy van-e bemenő adat. Az Erray tömb elemeinek hosszát ezért megvizsgáljuk. Ha közülük valamelyiknek a hossza nulla, akkor azt úgy értékeljük, hogy az aktuális és a következő események hiányoznak, ezért az aktuális ismétlés megszakad. A tömb elemértékeinek a fájlba írása addig folytatódik, amíg az üres elemértéket megtaláljuk. Azértékeket a csv fájlba a FileWrite() függvény írja. FileWrite() függvény int FileWrite(int handle,...) A függvény feladata az információk csv fájlba írása, az információk közötti elválasztók automatikus elhelyezését is beleértve. A "\r\n" sorzáró jelet az információ írása után adju khozzá a fájlhoz. A számszerű információt átalakítjuk szöveges formátumba a végrehajtás során (lásd: Print() függvény). A függvény visszaküldi az írott szimbólumok számát vagy negatív értéket, ha hiba történik. Paraméterek: handle - állományleíró, amit a FileOpen() függvény küld vissza;... - az adatokat vesszővel választjuk el. A paraméterek száma nem lehet több 63-nál. A "double", "int" típusú adatokat automatikusan átalakítjuk string értékké (a "color", "datetime" és "bool" típusú adatokat egész (int ) adatoknak tekintjük és szintén stringé alakítjuk, a string típusú adatokat átalakítás nélkül, ahogy vannak úgy írjuk. A tömböket nem adhatjuk meg paraméterekként; a tömbök értékeit elemenként kell megadni. A szóban forgó példában az információt a következő módon írjuk a fájlba: Qnt_Symb=FileWrite(Handle,Erray[i,0],Erray[i,1]);//Writing to the file Az elválasztót (azt a szimbólumot, amit elválasztóként használunk, a FileOpen() fájlnyitó függvényben megadtuk, (ebben az esetben: ';') és az Erray[i,0] értékek után fogjuk írni a fájl írása során. A "\r\n" sorzáró jelet automatikusan elhelyezi a FileWrite() függvény a függvényvégrehajtás végén, vagyis az írás végén. A for" ciklus mindegyik végrehajtásakor egy bejegyzést fog írni. Minden új bejegyzés attól a pozíciótól kezdődik, ahol az előző bejegyzés elválasztóját elhelyezte. Azután az Erray következő elemeinek az értékeit fogja írni a fájlba (az elemeket indexe 1-el növekszik minden ismétlésnél). Ha az aktuális bejegyzés fájlba írása sikeres a vezérlés elvégzi a következő ismétlést. Ha a fájlban írás sikertelen, a FileClose() függvény a fájlt be fogja zárni, és a felhasználó értesítése után a start() függvény

307 befejezi a működését. Ha minden fájlba írást sikeresen végrehajtottunk, akkor a "for" ciklusnak vége van, és a vezérlést a 7-8 blokkban megkapja a FileClose() fájlbezáró-függvény. Ebben az esetben a sikeres fájllétrehozásról látunk üzenetet és azután a start() függvény végrehajtása véget ér. A 149. ábrán látható News.csv fájl létrehozása után az EA végrehajtás kész. Fájlműveleteket végrehajtó függvények Függvény Összefoglaló információ FileClose A korábban a FileOpen() függvénnyel megnyitott fájl bezárása. FileDelete A fájl törlése. Csak azokat a fájlokat törölhetjük, amelyek a terminal_folder\experts\files mappában, vagy tesztelés esetén a terminal_folder\tester\files mappában vagy ezek almappáiban találhatók. FileFlush FileIsEnding Kiírja a fájl input-output bufferben lévő információt a merevlemezre. TRUE-t küld vissza, ha a fájlmutató a fájl végén van, minden más esetben visszaküldi a FALSEot. Ha a fájl végét a fájlolvasás alatt érjük el, a GetLastError() függvény az ERR_END_OF_FILE (4099) hibát fogja visszaküldeni. FileIsLineEnding Visszaküldi a TRUE-t, ha a fájlmutató a CSV fájlban a sorvégén van. Különben visszaküldi a FALSE-ot. FileOpen Megnyit egy fájlt olvasásra vagy írásra. A függvény visszaküldi a megnyitott fájl állományleíróját, ha a megnyitás nem sikerül, akkor -1-et. FileOpenHistory Megnyit egy fájlt az aktuális hitory mappában (termial_folder\history\server_name) vagy az alkönyvtáraiban. A függvény visszaküldi a megnyitott fájl állományleíróját,vagy ha a megnyitás nem sikerül, akkor -1-et. FileReadArray A függvény a bináris állományból a meghatározott számú elemet olvassa ki egy tömbbe. A tömbnek az olvasás előtt elegendő méretűnek kell lennie. A függvény visszaküldi a ténylegesen kiolvasott elemek számát. FileReadDouble A függvény lebegőponttal olvassa a dupla pontosság számot (double) a bináris állomány aktuális pozíciójából. A szám mérete a következő lehet: 8 byte (double) és 4 byte (float). FileReadInteger A függvény kiolvassa az egész számot a bináris állomány aktuális pozíciójából. A szám mérete a következő lehet: 1, 2 vagy 4 byte. Ha a szám méretét nem adtuk meg, meg fogja próbálni azt 4 byte-os egész számként olvasni. FileReadNumber Számot olvas a CSV fájl aktuális pozíciójától addig, amíg elválasztóval nem találkozik. Csak csv fájloknál alkalmazhatjuk. FileReadString FileSeek FileSize FileTell FileWrite FileWriteArray A függvény olvassa a fájlból a stringet az aktuális pozíciótól. Ezt alkalmazhatjuk a csv és a bináris fájloknál is. A szövegállományt addig fogja olvasni, amíg elválasztóval nem találkozik. A bináris állományokból a meghatározott számú szimbólumot fogja kiolvasni. A függvény új pozícióba mozgatja a fájlmutatót, az elmozdulás kiindulási pontja a fájl kezdete, vége vagy az aktuális pozíciója. A következő olvasás vagy írás az új pozícióból kezdődik. Ha a mutató mozgatása sikeresen megtörtént, a függvény vissza fogja küldeni a TRUE-t, más esetben a FALSE-t. A függvény byte-okban küldi vissza a fájl méretét. A függvény visszaküldi a fájlmutató elmozdulását a fájl kezdetéből. A függvény az információt csv fájlba írja, az elválasztót automatikusan elhelyezi az információk között. A sorzáró jelet "\r\n" akkor kapja meg a fájl amikor az írás kész. A számszerű adatot az eljárás alatt átalakítja string formátumba. A függvény visszaadja az írott szimbólumok számát vagy egy negatív értéket, ha hiba történik. A függvény egy tömb értékeit írja bináris állományba. FileWriteDouble A függvény lebegőpontos számot ír a bináris állományba. FileWriteInteger A függvény egész számot ír a bináris állományba. FileWriteString A függvény bejegyzést ír a bináris állományba az aktuális pozíciótól. Visszaküldi a ténylegesen írott byte-ok számát vagy egy negatív értéket, abban az esetben ha hiba történt

308 Tömbök és előre definiált tömbök Nagyon fontos megjegyezni, hogy az MQL4-ben minden ilyen típusú elem indexálása mindig nullával kezdődik. Ezek alapján nem szabad összekeverni a tömbelem-index értékét a tömbben lévő elemek számával (lásd: Tömbök). Például, egy tömb deklarálása: int Erray_OHL[3]; // Array declaration Ez azt jelenti, hogy az egy dimenziós Erray_OHL tömb három elemet tartalmaz. Az elemek indexelése nullával kezdődik, az első elem indexe 0 (Erray_OHL [0]), a második indexe 1 (Erray_OHL[1]), és a harmadik indexe 2 (Erray_OHL[2]). A tömbelem index maximális értéke egyel kisebb a tömbben lévő elemek számánál. Ebben az esetben a tömb egy dimenziós, a maximális index 2, mert a tömb elemeinek a száma 3. Ugyanez a helyzet a tömbben lévő dimenziók számával is. Például, ha egy tömböt a következőképpen deklarálunk: int Erray_OHL[3][8]; // Array declaration Ennek a tömbnek két dimenziója van. Az első dimenzióban megadjuk a sorok számát (ebben a példában 3), és a második dimenzióban a sorokban lévő elemek számát (vagy az oszlopok számát, ami ebben a példában 8). A dimenziók számozása az indexekéhez hasonló. Az első dimenzió száma 0, és a másodiké 1. A dimenziók számát például az ArrayRange() függvényben használjuk. ArrayRange() függvény int ArrayRange(object array[], int range_index) A függvény visszaküldi a tömb megadott dimenziója elemeinek a számát. Az ArrayRange() függvény használatát a következő feladat megoldásával demonstrálhatjuk: 38. feladat: A Mas_1 tömb egy 3x5 mátrix értékeit tartalmazza. Adjunk a Mas_2 tömb elemeinek olyan értékeket melyek egyelőek az eredeti mátrix elemértékeivel. Önkényesen megválasztott értékekkel dolgozhatunk. Adjunk a mátrixoknak valamilyen kezdeti értéket és helyezzük el őket a Mas_1 és Mas_2 tömbökben a következő formában: Indexes Indexes Az átalakított mátrix, Mas_2 A kiindulási mátrix a Mas_1 tömbben. tömb ábra. A kiinduló és az átalakított mátrixok. Ebben az esetben a problémát úgy oldjuk meg, hogy a mátrix átalakítás szabályai szerint átírjuk az első mátrix értékeit a másodikba, mégpedig úgy, hogy az elemek az első mátrixoszlopokból a második mátrix soraiba kerülnek. A mátrix átalakítási feladat megoldását a matrix.mq4 expertben valósítjuk meg:

309 // // matrix.mq4 // The code should be used for educational purpose only. // int start() // Special function start() int Mas_1[3][5]=1,2,3,4,5, 11,12,13,14,15, 21,22,23,24,25; int Mas_2[5][3]; int R0= ArrayRange( Mas_1, 0); // Number of elements in first dim. int R1= ArrayRange( Mas_1, 1); // Number of elements in second dim. for(int i=0; i Két tömböt hozunk létre a start() függvényben. A Mas_1 tömbnek 3 sora van, amelyek 5 elemet tartalmaznak, és a MAS_2 tömbnek 5 sora, mindegyik sor 3 elemet tartalmaz. Az értékek átírását a következő lépésben hajtjuk végre: Mas_2[[j][i] = Mas_1[i][j]; // Matrix transposition Annak érdekében, hogy kiszámítsuk az ismétlések számát mindkét ciklusoperátorhoz, tudnunk kell mindkét tömb elemeinek a számát. Ebben a példában használhatnának a 3 és 5 állandókat. Mindazonáltal a programtervezésnek ez módszere helytelen. Egy program ettől eltérő értékeket is tartalmazhat, és ezeket az értékeket minden esetben kézzel beállítani nagyon körülményes. Egy programot úgy kell tervezni, hogy a szükséges módosításokat egy helyen tudjuk elvégezni, és az ettől függő módosításokat, ha szükséges, a program saját maga elvégezze. Ebben az esetben csak azokat az adatokat kell módosítani, amelyek létrehozzák és inicializálják a tömböket és nem szükséges a kódot módosítani az egyéb helyen. A Mas_1 tömb első és második dimenziója elemeinek a számát a következő számítások végrehajtásával határozzuk meg: int R0 = ArrayRange( Mas_1, 0); // Number of elements in first dim. int R1 = ArrayRange( Mas_1, 1); // Number of elements in second dim. Megjegyzés: a 0 értéket használjuk az első dimenzióra és az 1 értéket használjuk a másodikra. Az R0 és R1 változók értékeit arra használjuk, hogy meghatározzuk a "for" ciklusok ismétlései számát. A Mas_2 tömbelemek értékeit a Comment() függvény segítségével jelenítjük meg a képernyőn ábra. A matrix.mq4 végrehajtásának eredménye. A tömbműveleteket végző függvények Függvények ArrayBsearch Rövid leírás A függvény visszaküldi a tömb első dimenziójából az azt az indexet ahol a keresett értéket először megtalálta. Ha tömbelem a megadott értékkel nem létezik, akkor a függvény vissza fogja küldeni azt az indexet ahol a legközelebbi értékű elem található

310 ArrayCopy ArrayCopyRates ArrayCopySeries ArrayDimension Ez függvény tömböt másol egy másikba. A tömböknek ugyanazon típusúaknak kell lenniük. A double[], int[], datetime[], color[] és bool[] típusú tömböket ugyanolya típusú tömbökbe másolhatjuk. A függvény visszaküldi a másolt elemek számát. A függvény a báradatokat a RateInfo[][6] kétdimenziós tömbbe másolja és visszaküldi a másolt bárok számát. Ha a művelet nem sikerül -1-et küld vissza. A függvény egy előre definiált tömböt egy felhasználói tömbbe másol és visszaküldi a másolt elemek számát. Visszaküldi egy több dimenziós tömb dimenzióinak számát. ArrayGetAsSeries A függvény TRUE-t küld vissza, ha a tömb elrendezése olyan, mint az előre definiált tömböké (a tömb elemeit az utolsó elemtől az első felé indexelték), ha nem, akkor visszaküldi a FALSE-t. ArrayInitialize Beállít egy (azonos) értéket a tömb minden elemére. Visszaküldi az inicializált elemek számát. ArrayIsSeries Visszaküldi TRUE-t ha a vizsgált tömb előre definiált tömb (Time[], Open[],Close[],High[],Low[] Volume[]), különben visszaküldi a FALSE-t. ArrayMaximum ArrayMinimum ArrayRange ArrayResize Megkeresi a maximális értékű elemet. A függvény visszaküldi a tömb maximális elemének az indexét. Megkeresi a minimális értékű elemet. A függvény visszaküldi a tömb minimális elemének az indexét. Visszaküldi az elemek számát a tömb megadott dimenziójában. A dimenzió mérete 1-el nagyobb, mint a legnagyobb index, mert az indexelés nullával kezdődik. Beállítja a tömb első dimenziójának az új méretét. Visszaküldi a tömbelemek új számát, ha a függvény végrehajtása kész, ha a végrehajtás nem sikerül és a tömb mérete nem változik -1-et küld vissza. ArraySetAsSeries Beállítja az indexelés irányát a tömbben. ArraySize ArraySort Visszaküldi egy tömb elemeinek a számát. A számszerű tömböket az első dimenziójuk alapján sorba rendezi. Az előre definiált tömbök nem rendezhetők sorba. Az előre definiált tömbök elérési függvényei Függvények ibars ibarshift iclose ihigh ihighest ilow ilowest iopen itime ivolume Összefoglaló információ Visszaküldi a megadott charton lévő bárok számát. Idő alapján keres meg egy bárt. A függvény visszaküldi annak a bárnak az indexét, amely bárban a megadott időpont van. Ha a megadott időponthoz nem talál bárt ( lyuk az előtörténetben), az exact paramétertől függően vagy az időponthoz legközelebbi bár indexét vagy -1-et küld vissza. Visszaküldi a shift paraméterrel meghatározott bár záró árát az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik. Visszaküldi a shift paraméterrel meghatározott bár maximum árát az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik. Visszaküldi az adott intervallumon belüli megtalált maximális érték indexét (az aktuális bárhoz viszonyítva). Visszaküldi a shift paraméterrel meghatározott bár minimum árát az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik. Visszaküldi az adott intervallumon belül megtalált minimális érték indexét (az aktuális bárhoz viszonyítva). Visszaküldi a shift paraméterrel meghatározott bár nyitó árát az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik. Visszaküldi a shift paraméterrel meghatározott bár nyitási idejét az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik. Visszaküldi a shift paraméterrel meghatározott bárban lévő tickek számát az aktuális chartról (szimbólum, időkeret) 0-t küld vissza, ha hiba történik

311 Matematikai függvények Az MQL4 matematikai és trigonometriai függvényeket is tartalmaz. Némelyik függvény használata nagyon egyszerű. Például a MathMax() függvény visszaküldi a paraméterlistában megadott két érték közül a nagyobbat. Néhány függvény használata bizonyos figyelmet és körültekintést követel. Vizsgáljuk meg az egyik ilyen függvényt! MathFloor() függvény double MathFloor(double x) A függvény visszaküldi azt a legnagyobb egész számot, ami kisebb vagy egyenlő, mint x. Paraméterek: x - számszerű érték. Vegyük észre, hogy az érték, amit visszaküldött a függvény valós szám (double típus), ugyanakkor az előbb azt írtuk, hogy a függvény visszaküld egy egész számot. Ez azért lehetséges, mert a függvény egy olyan valós számot küld vissza, aminek a tizedespont utáni pozícióiban csak nullák szerepelnek. Például a MathFloor() függvény visszaküldhet 37.0-t (double típusú pozitív szám) vagy -4.0-et (a double típus negatív szám). A leírásban az is szerepel, hogy a függvény a megadott számhoz legközelebbi nálánál kisebb egész számot küldi vissza. Például, ha az adott x paraméter értéke 13.5, a legközelebbi kisebb egész szám a Vagy ha x értékének a negatív értéket adjuk a függvényparaméterben, a legközelebbi kisebb egész szám egyenlő el. A függvényparaméter előjelének módosítása abszolút értékben különböző eredményeket adhat. Az ilyen függvények használata néhány esetben nagyon kényelmes. Például, vizsgáljuk meg az új megbízások megadásakor a függvény használatát a kötésméret meghatározására. int Percent =30; // % of free margin double Free =AccountFreeMargin(); // Free margin double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);// 1 lot price double Step =MarketInfo(Symb,MODE_LOTSTEP); // Size step changed double Lots_New=MathFloor(Free*Percent/100/One_Lot/Step)*Step; A Percent paraméter értékét a felhasználó állíthatja be. Ebben az esetben a felhasználó a szabad margin 30 százalékát engedélyezi felhasználásra. A dealing center szabályai szerint, a megbízás lot méretének oszthatónak kell lennie a kötésméret megváltoztatásának minimális lépése által (Step). Az 1 lot (One_Lot) kötésmérethez szükséges szabad margin (Free) értékének ismerete szintén nélkülözhetetlen a számításhoz. Ismerjük meg a programozó logikáját, amikor összeállította azt a kifejezést, amivel számítható a Lots_New kötésméret. A szemléltetés kedvéért adjunk a változóknak számszerű értékeket! Legyen Free=5000.0, One_Lot= (A legtöbb dealing center hasonló nagyságú fedezetet követel sok devizapárra), Step=0.1. Ebben az esetben, a Lots_New értékét meghatározó kifejezést a következőképpen tudjuk fölírni: Lots_New = MathFloor(5000.0*30/100/1360.0/0.1)*0.1; Az *30/100 kifejezés az új megbízás nyitásához szükséges pénz mennyisége. Ebben az esetben az új megbízásra fedezet áll rendelkezésre. Ez a fedezet / = lot kötésméretre elegendő. Azonban a dealing center nem fogad el ilyen összegű megbízást, mert a minimális lépésköz Step=0.1 (ez a leggyakoribb). A helyes kötésméret meghatározásához el kell dobni mindent szükségtelen számjegyet a tizedes jegy után, és le kell cserélni őket nullákra. Erre a célra használhatjuk a szóban forgó matematikai függvényt: Lots_New = MathFloor( /0.1)*0.1; A MathFloor( /0.1) értéke 11.0 lesz, és a Lots_New változó kiszámított értéke 1.1 lot lesz. Ez az érték megfelel a dealing center követelményeinek és ezért az új megbízás összegeként használhatjuk

312 Matematikai függvények Függvény MathAbs Összefoglaló információ A függvény visszaküldi az adott szám abszolút értékét. MathArccos A függvény visszaküldi az x arc cosinus értékét az 0-π radián tartományban. Ha x kisebb, mint -1 vagy nagyobb 1-nél a függvény meghatározatlan értéket küld. MathArcsin A függvény az x arc sinus értékét küld vissza a -π/2 - π/2 radián tartományban. Ha x kisebb mint -1 vagy nagyobb 1-nél a függvény meghatározatlan értéket küld vissza. MathArctan A függvény az x arc tangensét küldi vissza. Ha x egyenlő 0-val, a visszatérési érték 0. A MathArctan függvény a -π/2 - π/2 tartományban küld vissza az értéket. MathCeil A függvény visszaküldi azt a legkisebb egész számot, ami nagyobb vagy egyenlő mint x. MathCos MathExp A függvény visszaküldi a szög koszinuszát. A függvény visszaküldi az e értékének d-edik hatványát. A túlcsordulásnál a függvény INF-et küld vissza (infinity, végtelen), és az alulcsordulásnál 0-t. MathFloor A függvény visszaküldi azt a legnagyobb egész számot, ami kisebb vagy egyenlő, mint x. MathLog MathMax A függvény visszaküldi x természetes logaritmusát. Ha x negatív, a függvény NaN értéket küld vissza (meghatározatlan érték). Ha x = 0, visszaküldi az INF-et (végtelen). A függvény visszaküldi két számszerű érték közül a nagyobbat. MathMin A függvény visszaküldi két számszerű érték közül a kisebbet. MathMod A függvény visszaküldi két szám hányadosának a maradékát. A MathMod függvény kiszámítja az x / y hányados f maradékát, ahol x = i * y + f, ahol i egy egész szám, f ugyanolyan típus, mint x és f abszolút értéke kisebb y abszolút értéknél. MathPow Visszaküldi az alapkifejezés adott kitevőre (exponens) emelt hatványát. MathRand A függvény generál egy ál-véletlen egész számot a tartományán belül. A MathSrand függvénnyel hozzuk létre az ál-véletlen szám kiindulási értékét a generálás előtt. MathRound A függvény visszaküldi a megadott számszerű érték legközelebbi egész számára kerekített értékét. MathSin MathSqrt MathSrand MathTan A függvény visszaküldi a megadott szög szinuszát. A függvény visszaküldi az x négyzetgyökét. Ha x negatív, a MathSqrt függvény NaN értéket küld vissza (meghatározatlan érték). A függvény beállítja az ál-véletlen szám létrehozásának kiindulási értékét. MathTan függvény visszaküldi az x tangensét. Ha x nagyobb vagy egyenlő, mint 263, vagy kevesebb, vagy egyenlő, mint -263 akkor az eredmény értelmezhetetlen. Ebben az esetben a függvény egy határozatlan értéket küld vissza

313 A globális változók függvényei Az ügyfélterminál globális változóival kapcsolatban már sok függvény működését leírtuk a Globális változók részben. Itt megemlítettük azt is, hogy egy helyesen tervezett programnak törölnie kellett a globális változóit, amikor befejezi a működését. A GV-knek nem kell megmaradniuk az ügyfélterminálban a program bezárása után. Az olyan programok hibakeresése során, amik használják az ügyfélterminál globális változóit néhány GV a terminálban maradhat. Ebben az esetben a tesztelendő program következő indítása előtt a programozónak kézzel kell törölnie a GV-ket. Ha automatizálni akarjuk ezt az eljárást, létre kell hozni egy olyan scriptet, ami törli az ügyfélterminál minden globális változóját. GlobalVariablesDeleteAll() függvény int GlobalVariablesDeleteAll(string prefix_name=null) Törli a globális változókat. Ha prefixumot nem adtunk meg paraméterként, akkor minden elérhető globális változót törölni fog. Egyébként csak azokat a GV-ket fogja törölni melyeknek a neve a megadott prefixummal kezdődi. A függvény visszaküldi a törölt változók számát. Paraméterek: prefix_name - a törlendő globális változók neveinek prefixuma. Lent egy példa, az egyszerű deleteall.mq4 script, ami törli az ügyfélterminál minden globális változóját. // // deleteall.mq4 // The program is intended to be used as an example in MQL4 Tutorial. // int start() // Special start() function GlobalVariablesDeleteAll(); // Deleting of all GVs PlaySound("W2.wav"); // Sound return; // Exit // A scriptet csak akkor indíthatjuk el, ha olyan programok, amik GV-t használnak, nem futnak az ügyfélterminálban. Különben a script futtatása felboríthatja a többi futó program logikáját és ez ellenőrizetlen folyamatokhoz vezethet. A script végrehajtása után a globális változók ablaka (F3) az ügyfélterminálban üres lesz: 153. ábra. Az ügyfélterminálban levő Global Variables ablak a deleteall.mq4 script végrehajtása után

314 Függvények a GV-k használatához Függvény GlobalVariableCheck GlobalVariableDel GlobalVariableGet GlobalVariableName Összefoglaló információ Visszaküldi a TRUE-t, ha a GV elérhető. Ha nem akkor FALSE-t küld vissza. Töröl egy globális változót. Visszaküldi a TRUE-t, ha a változót sikeresen törölte. Különben FALSE-t küld vissza. Visszaküldi egy globális változó értékét, vagy 0-t küld vissza, ha hiba történt. A függvény az indexe alapján visszaküldi egy globális változó nevét a globális változók listájából. GlobalVariableSet Beállít egy új értéket egy globális változóra. A rendszer létre fog hozni egy új változót, ha a változó még nem létezik. Az utolsó hozzáférés idejét fogja visszaküldeni, ha a függvény végrehajtása sikerült. Különben 0-t küld vissza. GlobalVariableSetOnCondition Beállít egy új értéket a létező globális változóra, ha annak a jelenlegi értéke egyenlő a check_value (harmadik) paraméter értékével. A függvény létre fogja hozni az ERR_GLOBAL_VARIABLE_NOT_FOUND (4058) hibakódot és vissza fogja küldeni a FALSE-t ha változó nem létezik. Visszaküldi TRUE-T, ha a függvény sikeresen futott. Különben visszaküldi a FALSE-t. GlobalVariablesDeleteAll Törli a globális változókat. Ha prefixumot nem adtunk meg paraméterként, akkor minden létező globális változót törölni fog. Egyébként csak azokat a GV-ket fogja törölni melyeknek a neve a megadott prefixummal kezdődi. A függvény visszaküldi a törölt változók számát. GlobalVariablesTotal A függvény a globális változók teljes számát küldi vissza

315 Egyéni indikátorok Az egyéni indikátorok függvényei lehetővé teszik, hogy kényelmesen elvégezzük a szükséges beállításokat az egyéni indikátorokon. Vizsgáljunk meg közülük néhányat (lásd még: Egyéni indikátorok létrehozása). SetIndexBuffer() függvény bool SetIndexBuffer(int index, double array[]) A függvény az előzőleg létrehozott indikátor bufferhez globális szinten hozzárendel egy indikátorvonalat. Az indikátor buffereket az IndicatorBuffers() függvény segítségével hozzuk létre, az indikátor bufferek (indikátorvonalak) száma nem lehet több mint 8. A sikeres végrehajtás esetén visszaküldi a TRUE-t másképp visszaküldi a FALSE-t. A hibával kapcsolatos részletes információt a GetLastError() függvény végrehajtásával kaphatunk. Paraméterek: index - az indikátorvonal indexe (értéke 0-tól 7-ig lehetséges); array[] - az indikátor bufferhez tartozó tömb neve. SetIndexStyle() függvény void SetIndexStyle(int index, int type, int style=empty, int width=empty, color clr=clr_none) A függvény beállítja vagy módosítja az indikátorvonalak kinézetét. Paraméterek: index - az indikátorvonal indexe (az értéke 0-tól 7-ig lehetséges); type az indikátorvonal-típusa. Egy típus az indikátorvonalak típusának listájáról (lásd: Az indikátorvonalak stílusai); style a vonalstílus csak az 1 pixel szélességű vonalra használható. Egy vonalstílus a lehetséges indikátorvonal stílusok közül. Az EMPTY (alapértelmezett) érték esetén a stílus nem fog megváltozni (folytonos vonal); width vonal szélesség; értéke lehet - 1,2,3,4 vagy 5; az EMPTY érték esetén a szélesség változatlan marad; clr a vonal színe. A CLR_NONE üres érték azt jelenti, hogy a szín nem fog megváltozni (a #property indicator_colorn által meghatározott marad). SetIndexLabel() függvény void SetIndexLabel(int index, string text) A függvény lehetővé teszi, hogy egy indikátor nevét láthassuk a Data Ablakban és az előugró buborékban. Paraméterek: index - az indikátorvonal indexe (az értéke 0-tól 7-ig lehetséges); text - egy szöveg, ami leír egy indikátor vonalat. NULL azt jelenti, hogy a vonal értékét nem láthatjuk a Data Ablakban. Példa egy egyszerű indikátorra (indicatorstyle.mq4), ami a bárok maximum értékeit mutatja és használja a fent leírt függvényeket:

316 // // indicatorstyle.mq4 // The code should be used for educational purpose only. // #property indicator_chart_window // Indic. is drawn in the main window #property indicator_buffers 1 // Amount of buffers #property indicator_color1 Blue // Color of the first line double Buf_0[]; // Indicator array opening // int init() // Special function init() SetIndexBuffer(0,Buf_0); // Assigning the array to the buffer SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style SetIndexLabel (0,"High Line"); return; // Exit spec. function init() // int start() // Special function start() int i, // Bar index Counted_bars; // Amount of calculated bars Counted_bars=IndicatorCounted(); // Amount of calculated bars i=bars-counted_bars-1; // Index of the first uncounted while(i>=0) // Cycle for the uncounted bars Buf_0[i]=High[i]; // value 0 of the buffer on ith bar i--; // Index calculation for the next bar return; // Exit spec. function start() // Az 1-2 blokk tartalmazza az indikátor általános beállításait. A #property direktívákkal megadjuk, hogy az indikátort a fő ablakban kell megjeleníteni, az indikátor egy buffert használ, az indikátorvonal színe kék. Ezen kívül az 1-2 blokkban létrehozunk egy buffer is. A fenti függvényeket a 2-3 a blokkban használjuk. A bejegyzés: SetIndexBuffer(0,Buf_0); // Assigning the array to the buffer társítja a 0 indexű indikátor vonalhoz a Buf_0 buffer. A következő: SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style meghatározza a 0 indexel rendelkező indikátorvonal stílusát. A DRAW_LINE állandó azt jelzi, hogy egyszerű vonalat rajzolunk, a STYLE_SOLID állandó mutatja, hogy a vonal folytonos, és a vonalvastagság 2. A következő bejegyzésben: SetIndexLabel (0,"High Line"); nevet adhatunk a 0 indexű indikátorvonalnak. A megadott nevet láthatjuk a Data Ablakban és a chart ablakban felbukkanó buborékban. Az indikátorvonalak felcímkézése különösen akkor előnyös, ha az ablak sok indikátort tartalmaz; néha ez az egyetlen lehetőség, hogy megkülönböztessük az egyik vonalat a másiktól. 3-4 blokkban elvégezzük az indikátortömb elemértékeinek a kiszámítását, amelyek a bárok maximális értékeit mutatják (az indikátortömb kiszámítását leírtuk az Egyéni indikátorok létrehozása részbenl). Ha az indikátort egy különálló ablakban ábrázoljuk, akkor vízszintes szintvonalakat is létrehozhatunk ebben az ablakban. SetLevelValue() függvény void SetLevelValue(int level, double value) A függvény a segéd indikátorablakban létrehoz egy vízszintes szintvonalat

317 Paraméterek: level a szint sorszáma (0-31). value - a megadott szint értéke. A vízszintes szintek használata célszerű lehet, ha vizuálisan kell érzékelni, hogy az indikátorvonal a megadott érték alatt vagy fölött van. Azt az egyszerű indikátort, aki kiszámítja a bár maximum és minimum ára közti különbséget, lent mutatjuk be. A piaci események akkor érdekesek a felhasználónak (ebben a példában) ha az indikátorvonal a 0.001érték fölött vagy a érték alatt van. A példa azt az indikátor mutatja, ami ábrázolja a High és Low közti különbséget (linelevel.mq4): // // linelevel.mq4 // The code should be used for educational purpose only. // #property indicator_separate_window // Indic. is drawn in a sep. window #property indicator_buffers 1 // Amount of buffers #property indicator_color1 Red // Line color double Buf_0[]; // Indicator array opening // int init() // Special init() function SetIndexBuffer(0,Buf_0); // Assigning the array to the buffer SetIndexStyle (0,DRAW_LINE,STYLE_SOLID,2);// Line style SetIndexLabel (0,"High/Low Difference"); SetLevelValue (0, ); // The horizontal line level is set SetLevelValue (1, ); // Another level is set return; // Exit from spec.init() function // int start() // Special start() function int i, // Bar index Counted_bars; // Amount of calculated bars Counted_bars=IndicatorCounted(); // Amount of calculated bars i=bars-counted_bars-1; // Index of the first uncounted while(i>=0) // Cycle for the uncounted bars Buf_0[i]=High[i]-Low[i]; // 0 value of the buffer on bar i if(open[i]>close[i]) // if the candle is black.. Buf_0[i]=-Buf_0[i]; //.. then reverse the value i--; // Index calculation for the next bar return; // Exit from spec.start() function // A szóban forgó függvényt az indikátor 2-3 blokkjában használjuk. Ebben az esetben két vízszintes szintet határozunk meg. Az első érték a paraméterek listájában a vízszintes szint sorszáma, a második a szint értéke: SetLevelValue (0, ); // The level of the horiz. line is set SetLevelValue (1, ); // Another level is set Az indicatorstyle.mq4 és linelevel.mq4 indikátorok paramétereit úgy állítottuk be, hogy a szimbólumablakban és a Data Ablakban is láthatók legyenek

318 154. ábra. Az indikátor kinézete az ügyfélterminál különböző ablakaiban. Két ablak - a Data ablak és a chart ablak látható a 154. ábrán. Megfigyelhető a High Line szöveg és az érték a Data Ablakban. Ugyanazokat a bejegyzéseket láthatjuk az előugró szövegbuborékban is. A Data Ablakban az indikátor működése alatt, az indikátorvonal neve változatlan, de az értéke a bejegyzés jobb oldalán a charton lévő kurzorpozíciótól függ. Az előugró buborékban lévő indikátorvonal-név annak a vonalnak a neve, amelyik vonalon az egérkurzor van. A linelevel.mq4 indikátor segédablaka tartalmazza azokat a vízszintes szintvonalakat, amelyek a felhasználó beállításai szerint lettek elhelyezve. Ha a piros indikátorvonal fölött mozgatjuk a kurzort, akkor ennek az indikátorvonalnak a neve a "Difference between High and Low" és a kurzornál lévő értéke látható a felbukkanó ablakban. Az egyéni indikátorok függvényei Függvény Összefoglaló információ IndicatorBuffers Lefoglalja az indikátorvonalak értékeit tartalmazó bufferek számára szükséges memóriaterületet. A bufferek száma nem lehet több 8-nál, és kevesebb annál, mint amit a #property indicator_buffers direktívában megadtunk. Ha az egyéni indikátornak több bufferre van szüksége számításhoz, ezzel a függvénnyel hozhatjuk létre a szükséges buffereket. IndicatorCounted IndicatorDigits A függvény visszaküldi azon bárok számát, amelyek nem változtak meg az utolsó indikátorindítás óta. A bárok többsége nem igényel újraszámítást. Beállítja a pontosságot (a tizedespont utáni karakterek számát) az indikátorértékek megjelenítésekor. IndicatorShortName Beállít egy rövid nevet az indikátornak, az indikátor segédablakban és a Data Ablakban ezt a nevet láthatjuk. SetIndexArrow SetIndexBuffer SetIndexDrawBegin A DRAW_ARROW stílusú indikátorvonalak rajzolásához kijelöl egy szimbólumot. A függvény az előzőleg létrehozott indikátor bufferhez globális szinten hozzárendel egy indikátorvonalat. Beállítja annak a bárnak az indexét ahol az indikátorvonal rajzolását el kell kezdeni. SetIndexEmptyValue Beállítja az indikátorvonal üres értékét. Az üres értékeket nem rajzolja a terminál és nem mutatja a Data Ablakban. SetIndexLabel SetIndexShift SetIndexStyle SetLevelStyle SetLevelValue Nevet ad az indikátorvonalaknak, ezt a nevet látjuk a Data Ablakban és a szöveg buborékban. Beállítja az indikátorvonal bárokhoz viszonyított helyzetét. A pozitív érték jobbra fogja elmozdítani az indikátorvonalat, a negatív érték balra. Vagyis az aktuális bárra kiszámolt értéket a shifttel eltolt bárra rajzolja a terminál. Új vonaltípust: vonalstílust, vastagságot és színt ad a meghatározott indikátorvonalnak (lásd: Az indikátorvonalak stílusai). A segédablakban lévő indikátor szintvonal stílusát állítja be (lásd: Az indikátorvonalak stílusai). A segéd indikátorablakban létrehoz egy vízszintes szintvonalat

319 Számlainformáció Ezeket a függvényeket egyszerűen használhatjuk az ügyfélterminál felügyeletére, például arra, hogy korlátozzuk a programunk használatát a kereskedés során. Vizsgáljuk meg, hogyan lehet megoldani egy ilyen feladatot: 39. feladat: Hozz létre egy biztonsági kódot a kereskedelmi terjesztésre szánt programodhoz, ami megfelel a következő követelményeknek: a programnak kérnie kell egy jelszót az egyéni ügyfelek valódi számláihoz; ne kérjen jelszót bizonyos brókerekkel történő kereskedés esetén; a demószámlákon ne legyen korlátozva a program használata. Ez a példa egy valós problémafelvetést tartalmaz. A program sikeres kereskedelmi terjesztéséhez a potenciális vevőknek lehetővé kell tenni, hogy tesztelhessék azt egy demószámlán. Tehát a felhasználó meg ismerheti a program előnyeit és el tudja dönteni, hogy megvegye-e azt. Egy általános jelszó alkalmazása azért helytelen, mert a program jogosulatlan terjesztését ebben az esetben nem tudjuk megakadályozni. Tehát az egyéni jelszónak függenie kell a felhasználó igazi számlaszámtól. Nem szükséges bizonyos vállalat ügyfeleire használni a jelszót (ha a dealing center minden ügyfelének megvette a program használati jogát) - a programnak azonosítania kell a szervert ahová az ügyfélterminál kapcsolódik. És, ha ez engedélyezett vállalat szervere, minden felhasználójának lehetővé kell tenni, hogy akadályok nélkül dolgozzon a programmal. A 39. feladatban szereplő programhasználati jogot korlátozó megoldást láthatjuk lentebb (сheck.mq4 EA): // // сheck.mq4 // The code should be used for educational purpose only. // extern int Parol=12345; // int start() // Special function 'start' if(check()==false) // If the using conditions do not.. return; //..meet the requirements, then exit // The main code of the program must be specified here Alert("Program execution"); // Alert example return; // Exit start() // bool Check() // User-defined function of.. //.. use conditions checking if (IsDemo()==true) // If it is a demo account, then.. return(true); //.. there are no other limitations if (AccountCompany()=="SuperBank") // The password is not needed return(true); //..for corporate clients int Key=AccountNumber()* ; // Calculating key if (Parol==Key) // If the password is correct, then.. return(true); //..allow the real account to trade Alert("Wrong password. EA does not work."); return(false); // Exit user-defined function //

320 Az ellenőrzést közvetlenül a különleges start() függvény kezdetekor végezzük el, ebben a példában a 2-3 blokkban: if(check()==false) // If the using conditions.. Ha a Check() felhasználói függvény visszatérési értéke false akkor a vezérlést megkapja a return operátor és a különleges start() függvény befejezi a működését. A program fő kódja közvetlenül ezután következik. Ha a Check() függvény true értéket küld vissza (az ellenőrzés sikeres), akkor a fő kódot végrehajtásra kerül. A Check() felhasználói függvényben három ismertetőjelet vizsgálunk: - a számla egy demó számla-e; - az ügyfél a megfelelő vállalati szerveren kereskedik; - a jelszó érvényes a valódi számlára. Az első ismertetőjel ellenőrzésére az IsDemo() függvényt használjuk. IsDemo() függvény bool IsDemo() A függvény visszaküldi a TRUE-t, ha a program egy demószámlával működik. Különben visszaküldi a FALSE- OT. Ha az IsDemo() függvény TRUE értéket küld vissza, akkor a felhasználó demó-számlával dolgozik. Ez azt jelenti, hogy nincs szükség további ellenőrzésre, (mert demó számlán a program használata nincs korlátozva). A Check() függvény ebben az esetben befejezi a működését és visszaküldi a TRUE értéket: if (IsDemo() == true) // If it is a demo account, then.. return(true); //.. there are no other limitations De ha az IsDemo() függvény false értéket küld vissza, a felhasználó valódi számlával dolgozik. Ebben az esetben meg kell vizsgálni, hogy a felhasználónak van-e elég jogosultsága. Az AccountCompany() függvényt ebben a példában arra használjuk, hogy ellenőrizze a kereskedő melyik bróker ügyfele. AccountCompany() függvény string AccountCompany() A függvény visszaküldi a bróker nevét ahol kereskedünk. Ha az ellenőrzés eredménye: if (AccountCompany() == "SuperBank")// The password is not needed.. return(true); //..for corporate clients az, hogy a vállalat neve egyezik azzal, amit a programban megadtunk, a Check() függvény be fogja fejezni a működését és a visszatérési értéke true lesz. Ha az ügyfél egy másik brókerhez csatlakozik (nem szerződött bróker ügyfele), akkor azt kell ellenőrizni, hogy van-e egyéni engedélye. A bejegyzés: int Key = AccountNumber()* ;// Calculating key a programozó elhelyez egy algoritmust a kulcs kiszámításához. Ez a példa egy egyszerű módszert tartalmaz. A programozó ennél összetettebb módszert is alkalmazhat a kulcs kiszámítására. Az algoritmusnak meg kell határoznia a számlaszámot, ehez az AccountNumber() függvényt használja

321 AccountNumber() függvény int AccountNumber() A függvény visszaküldi a folyószámla számát. A jelszó ellenőrzése a korábban megadott algoritmus alapján történik. Ha az ellenőrzés során a jelszó és a kulcs illeszkedik, akkor a Check() függvény befejezi a működését és true értéket küld vissza: if (Parol == Key) // If the password is correct, then.. return(true); //..allow the real account to trade Ha az ellenőrzés sikertelen, akkor a felhasználó nem tud valódi számlán kereskedni. Ebben az esetben a Check() függvény befejezi a működését és false értéket küld vissza és bemutatja a megfelelő üzenetet. Ebben az esetben a program jogosulatlan használatát megakadályozzuk. AZ ügyfélterminál információt visszaküldő függvények Függvény TerminalCompany TerminalName TerminalPath Összefoglaló információ Visszaküldi annak a brókernek a nevét, ahová az ügyfélterminál csatlakozott. Visszaküldi az ügyfélterminál nevét. Visszaküldi az ügyfélterminál telepítési mappájának elérési útját. Függvények, amik észlelik az ügyfélterminál aktuális állapotát és a végrehajtott MQL4 program környezeti státuszát Függvény GetLastError IsConnected IsDemo IsDllsAllowed IsExpertEnabled IsLibrariesAllowed IsOptimization IsStopped IsTesting IsTradeAllowed Rövid leírás A függvény visszaküldi az utolsó hibakódot, majd a különleges last_error változó értékét, ami az utolsó hibakódot tartalmazza beállítja nullára. Ezért a GetLastError függvény következő hívása 0-t fog visszaküldeni. Visszaküldi az ügyfélterminál és a szerver közötti kapcsolat státuszát, a visszaadott érték TRUE, ha a kapcsolat fennáll és FALSE, ha nincs kapcsolat a szerverrel vagy a kapcsolat megszakadt. Visszaküldi TRUE-t, ha a program egy demó-számlával működik. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha a DLL hivás az EA-nak engedélyezett. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha az EA futtatása engedélyezett az ügyfélterminálban. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha az EA számára engedélyezett, hogy egy könyvtári függvényt deklaráljon. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha az EA teszt módban dolgozik, és eközben optimalizál. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t ha a program (EA vagy script) parancsot kapott hogy fejezze be a működését. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha az EA tesztelő módban dolgozik. Különben visszaküldi a FALSE-t. Visszaküldi a TRUE-t, ha az EA-nak engedélyezett a kereskedés. Különben visszaküldi a FALSE-t. IsTradeContextBusy Visszaküldi a TRUE-t, ha a rendszer elfoglalt. Különben visszaküldi a FALSE-t. IsVisualMode UninitializeReason Visszaküldi a TRUE-t, ha az EA-t vizuális módban tesztelik. visszaküldi a FALSE-t. Visszaküldi az EA, az egyéni indikátor, vagy a script bezáródása okának a kódját. A visszaadott érték a deinitialization codes közül az egyik lehet. Ezt a függvényt az init() függvényből hívhatjuk, hogy elemezze az előző indítás deinicializálásának az okát

322 Az aktív számlával kapcsolatos információk elérésének függvényei Függvény AccountBalance AccountCredit AccountCompany AccountCurrency AccountEquity AccountFreeMargin Rövid leírás Visszaküldi az aktív számla egyenlegének az értékét (a teljes mennyiségű pénzt a számlán). Visszaküldi az aktív számla credit összegét. Visszaküldi a számlát kezelő brókercég nevét. Visszaküldi a letétiszámla deviza nevét. Visszaküldi a folyószámla sajáttőke-értékét. A sajáttőke-számítás a szerverbeállításoktól függ. Visszaküldi a szabad margin értékét, amit új megbízás nyitására használhatunk. AccountFreeMarginCheck Visszaküldi a szabad margin értékét, ami akkor maradna, ha a megadott megbízást végrehajtanánk. AccountFreeMarginMode A megbízás nyitásához szükséges szabad margin kiszámításának módja az adott folyószámlán. AccountLeverage AccountMargin AccountName AccountNumber AccountProfit AccountServer AccountStopoutLevel AccountStopoutMode Visszaküldi a folyószámla tőkeáttételét. Visszaküldi a nyitott pozíciók fenntartásához szükséges margin összegét. Visszaküldi a folyószámla felhasználónevét. Visszaküldi a folyószámla számát. Visszaküldi a letéti pénznemben számolt profit értékét. Visszaküldi az aktív szerver nevét. Visszaküldi a StopOut szint értékét. Visszaküldi a StopOut szint számítás módját

323 Kereskedelmi függvények A kereskedelmi függvényeket két csoportba lehet sorolni - azokra a függvényekre, amelyek kereskedelmi megbízást adnak, és azokra, amelyek visszaadnak valamilyen, a kereskedésre jellemező értéket. Az MQL4- ben csak öt függvény van, ami kereskedelmi kérést küldhet a szervernek: OrderSend() a függőben levő megbízás megnyitása; OrderClose() - a megbízás zárása; OrderCloseBy() - az ellentétes megbízások zárása; OrderDelete() a függőben levő megbízások törlése; OrderModify() mindenféle megbízás módosítása. A fent felsorolt függvények alkalmazásának a rendjét részletesen leírtuk a Programming of Trade Operations részben. Azoknak a további függvényeknek a használatára, amelyek nem alakítanak ki megbízást szintén gyakran szükség van. Például amikor a megbízásokat előre meghatározott sorrendben kell bezárnunk. Ahhoz, hogy ezt megtehessük, a programnak elemeznie kell a meglévő megbízásokat - a megbízás típusát, a kötésméretet, a stop szinteket, stb. Vizsgáljunk meg néhány függvényt amelyekkel hozzájuthatunk a megbízásokkal kapcsolatos információhoz. OrderTotal() függvény int OrdersTotal() A függvény visszaküldi az összes nyitott és függőben levő megbízás számát. OrderTakeProfit() függvény double OrderTakeProfit() A függvény visszaküldi az aktuális megbízás előre beállított profitkivételi szintjét. A megbízást előzőleg ki kell választani az OrderSelect() függvénnyel. OrderProfit() függvény double OrderProfit() Visszaküldi a kiválasztott megbízás nettó nyereség értékét (a swapok és jutalék nélkül). Ez a lezárt megbízásoknál fix profit, nyitott megbízásoknál a realizálatlan nyereség/veszteség. A megbízást előzőleg ki kell választani az OrderSelect() függvénnyel. OrderLots() függvény double OrderLots() Visszaküldi a kiválasztott megbízás lot összegét. A megbízást előzőleg ki kell választani az OrderSelect() függvénnyel. Azt a program részletet, ami lekérdezi egy megbízás TakeProfit szintjét, a profit, és lot összegét lent láthatjuk: for (int i=0; i<orderstotal(); i++) // For all orders if((orderselect(i,select_by_pos)==true) // If next exists double ТР = OrderTakeProfit(); // TakeProfit of order double Profit= OrderProfit(); // Order profit double Lots = OrderLots(); // Amount of lots //...TP and profit values usage in the program... // End of the cycle body

324 Világos, hogy a szóban forgó függvényeknek (OrderTakeProfit (), OrderProfit() és OrderLots()) nincs semmilyen paramétere mint például a megbízás azonosítója, a visszatérési értéket ezen függvények paraméterei nem befolyásolják. A megbízások egyedi jellemzőinek (a beállított stop szintek, a lot összege) a meghatározásához először ki kell jelölni a szükséges megbízást és ezután a program tudni fogja, hogy a számításokat melyik megbízásra vonatkozóan hajtsa végre. Ezért mielőtt elindítjuk a számításokat, végre kell hajtani az OrderSelect() függvényt (lásd: Megbízások zárása és törlése). Az ezután végrehajtott kereskedelmi függvények a kiválasztott megbízás jellemzőit fogják visszaküldeni. A megbízás jellemzők helyes értékelésének nagy jelentősége van. Például, mikor meghatározzuk a megbízások bezárásának a sorrendjét, meg kell határozni azokat az ismertetőjeleket melyek alapján eldöntjük, hogy melyik megbízást kell legelőször zárni, és melyiket később. Vessünk egy pillantást egy egyszerű feladatra! 40. feladat: Jelenleg két nyitott Buy megbízásunk van egyetlen szimbólumon. Az elsőt es árnál nyitottuk 0.5 lot méretben, a másodikat as árnál 1 lot méretben. Az aktuális ár A kereskedelmi feltételek a Buy megbízások zárásának a szükségességét jelzik. El kell dönteni, hogy melyik megbízást zárjuk le először és melyiket másodszor. Nyilvánvalóan a profit az első megbízáson 108 pont, míg az a másodikon 8 pont. Bár az első megbízást kisebb lot összeggel nyitottuk, mint a másodikat, mégis az elsőn van a nagyobb profit, az első megbízás profitja 540 dollár, a második megbízás profitja 80 dollár. Első ránézésre úgy tűnhet, hogy az első megbízást célszerűbb először bezárni, mert ennek nagyobb profitja van. Azonban ez hibás elgondolás. Meg kell vizsgálni a lehetséges forgatókönyveket, hogy helyes döntést hozzunk. A megbízások befejezésének a sorrendje lényegtelen lenne, ha tudnánk, hogy az ár nem fog megváltozni a zárási folyamat alatt. Azonban az ár a két megbízás bezárása között megváltozhat. Tehát azt a megbízást, ami több veszteséget okozhat a negatív forgatókönyv esetén, kell először lezárni. Ha az ár egy pontot esik, az első megbízás profitja 5 dollárral fog csökkenni, míg az a másodiké 10 dollárral. Nyilvánvaló, a második megbízás több veszteséget okozna, úgyhogy először ezt kell zárni. Ebben az esetben a kötésméret volt a meghatározó szempont a megbízások zárási sorrendjének meghatározásakor. A pozitív forgatókönyvet itt nem vesszük számításba, mert a kereskedelmi ismertetőjelek kialakultak a programban, és ezek az ismertetőjelek a Buy megbízás zárását jelzik. Ha két azonos méretű megbízás zárása közül kell választani akkor más szempontot kell keresni az elsőnek zárandó megbízás kiválasztására. Például a távolságot az aktuális ár és a megbízások StopLoss értéke között. Ebben az esetben azt kell elemezni, hogy melyik megbízás okozna több veszteséget egy gyors ármozogás esetén. A válasz nyilvánvaló: azt kell előbb zárni(a két azonos méretű megbízás közül) amelynek a StopLossa távolabb van az aktuális ártól. Így elemezni lehet a prioritást a megbízások egyéb paraméterei alapján is, és össze tudod állítani az ismertetőjelek prioritás alapú listáját, amikor eldöntöd a megbízások befejezésének sorrendjét. Nem nehéz azonosítani azokat az ismertetőjeleket, amelyeket nem kell figyelembe venni. Ez például a nyitó ár (és a megbízás profitja). Valamennyi pénzünk a kereskedés minden pillanatában látható az ügyfélterminál Equity oszlopában. Ennek az értéknek a forrása nem fontos, nem baj, ha egyik vagy másik megbízásunk veszteséggel jár, ha a végeredmény profitot hoz. Egy megbízás minden jellemzőjét megismerhetjük, ha alkalmazzuk az alábbi függvényeket: Kereskedelmi függvények Function Execution Errors (ez nem függvény) Summary Info Bármelyik kereskedelmi művelet (OrderSend, OrderClose, OrderCloseBy, OrderDelete vagy OrderModify függvények) sikertelen végrehajtása esetén negatív jegyszámot vagy FALSE-t kapunk vissza. Megtudatjuk a hiba okát, ha használjuk a GetLastError függvényt. Minden hibát a megfelelő módon kell feldolgozni

325 OrderClose OrderCloseBy OrderClosePrice OrderCloseTime OrderComment OrderCommission OrderDelete OrderExpiration OrderLots Bezárja a pozíciót. Visszaküldi atrue-t, ha a függvény sikeresen ért véget. És FALSE-t küld vissza ha a függvény hibával zárult. Egy nyitott pozíciót zár be egy másik, ugyanazon a szimbólumon lévő, de ellentétes pozícióval. Visszaküldi atrue-t, ha a függvény sikeresen véget ért. És visszaküldi a FALSE-t, ha a függvény hibával zárult. Visszaküldi a kiválasztott megbízás záró árát. Visszaküldi a kiválasztott megbízás zárási idejét. Visszaküldi a kiválasztott megbízás megjegyzését. Visszaküldi a kiválasztott megbízás kiszámított jutalékát. Törli a korábban elhelyezett függőben levő megbízást. Visszaküldi a TRUE-t, ha a függvény végrehajtása sikeresen véget ért. És visszaküldi a FALSE-t, ha a függvény hibával zárult. Visszaküldi a kiválasztott függőben levő megbízás lejáratának a dátumát. Visszaküldi a kiválasztott megbízás lot összegét. OrderMagicNumber Visszaküldi a kiválasztott megbízást azonosító magic numbert. OrderModify OrderOpenPrice OrderOpenTime OrderPrint OrderProfit OrderSelect Módosítja a korábban nyitott és a függőben levő megbízások paramétereit. Visszaküldi atrue-t, ha a függvény végrehajtása sikeresen véget ért. És visszaküldi a FALSE-t, ha a függvény hibával zárult. Visszaküldi a kiválasztott megbízás nyitó árát. Visszaküldi a kiválasztott megbízás nyitási idejét. Egy rendszerinformációs bejegyzést ír a naplófájlba. Visszaküldi a kiválasztott megbízás nettó nyereség értékét (a swapok és jutalék nélkül). Ez a lezárt megbízásoknál fix profit, a nyitott megbízásoknál a realizálatlan nyereség/veszteség. A függvény kiválasztja a feldolgozandó megbízást. Visszaküldi atrue-t, ha a függvény végrehajtása sikeresen véget ért. És visszaküldi a FALSE-t, ha hiba történt. OrderSend Ezzel a függvénnyel helyezzük el a piaci vagy a függőben levő megbízásokat. Visszaküldi az új megbízás jegyszámát, amit a szerver adott, illetve sikertelenség esetén -1-et küld vissza. OrdersHistoryTotal Visszaküldi a lezárt pozíciók és törölt megbízások számát a folyószámla történelmében, amelyeket az ügyfélterminál tartalmaz. OrderStopLoss OrdersTotal OrderSwap OrderSymbol OrderTakeProfit OrderTicket OrderType Visszaküldi a kiválasztott megbízás veszteségszintjéhez (stop loss) tartozó záró árat. Visszaküldi az összes nyitott és függőben levő megbízás számát. Visszaküldi a kiválasztott megbízás swap értékét. Visszaküldi a kiválasztott megbízás szimbólumnevét. Visszaküldi a kiválasztott megbízás nyereség kivételéhez (take profit) tartozó záró árat. Visszaküldi a kiválasztott megbízás jegyszámát (ticket). Visszaküldi a kiválasztott megbízás típusát

326 Egy szokásos program létrehozása Miután egy programozó sok egyszerű MQL4 alkalmazási programot kódolt, általában egy összetettebb projektekbe kezd, hogy gyakorlati alkalmazás során kényelmesen használható programokat hozzon létre. Az egyszerűbb programok legalább két okból nem elégítik ki a kereskedő (programozó) szükségleteit: 1. Az egyszerű programok funkcionális korlátaik miatt nem látják el a kereskedőt minden szükséges információval és a kereskedést megvalósító eszközzel, ezért az ilyen programok használata nem elég hatékony. 2. Az egyszerű programok kódjának tökéletlensége nehezíti a továbbfejlesztésüket és a szolgáltatásaik kiterjesztését. Ebben a részben bemutatjuk egy kereskedő Expert Advisor megvalósításának lehetséges alternatíváját, hogy azt felhasználhasd a saját projektjeid alapjaként. Egy szokásos program szerkezete Egy programban a felhasználói függvények alkalmazása lehetővé teszi, hogy hatékony és rugalmas algoritmusokat hozz létre az információk feldolgozására. Az #include fordítási direktíva lehetővé teszi, hogy (az egyszer megírt és tesztelt) függvényeidet másik programokban is használd. Ezzel a módszerrel létre tudod hozni a saját könyvtáraidat vagy használni tudod más programozók nyílt forráskódú fejlesztéseit. A megbízások nyilvántartása Ebben a részben egy példát találunk: a Terminal() felhasználói függvényt, melyet egy különálló.mqh kiterjesztésű fájlban valósítunk meg. Az ilyen fájlok tartalma a fordítás során az #include direktíva hatására illesztődik be a programkódba. Adat függvény Ez a felhasználói függvény hozza létre az EA aktuális működésével kapcsolatos szöveges információ megjelenítését. Ez a függvény helyettesíti a Comment() függvény chart ablakbeli használatát. A függvény egy indikátorként létrehozott segédablakban jeleníti meg az információit. Eseménykövető függvény A kereskedő a kereskedés során nem mindig vesz észre minden eseményt. Ez az MQL4 program segít abban, hogy észlelj a kereskedési helyzetekben és feltételékben bekövetkező minden változást. Az Events() felhasználói függvény az #include utasítás hatására épül be az EA ba, és működése során egy másik include fájlnak, az Inform() fájlnak szóló függvényhívásokat hajt végre. A megbízás méretét meghatározó függvény Egy megbízás méretének a meghatározása fontos tőke/kockázatkezelési feladat. A Lot() felhasználói függvény egy kis példa ezen feladat megvalósítására. A kereskedési ismertetőjeleket meghatározó függvény A kereskedés legfontosabb mozzanata a piacra lépés és a pozíció zárás feltételeinek meghatározása. A kerekedési ismertetőjelek és szabályok feldolgozása minden Expert Advisor alapvető feladatát jelentik. A Criterion() felhasználói függvényt #include utasítás segítségével illesztjük be az EA kódjába. Bemutatjuk, hogy az EA az indikátorértékek alapján hogyan dönti el, hogy az aktuális helyzet megfelele-e valamely kereskedési ismertetőjelnek. Kereskedelmi függvények Az aktuális helyzetet elemeztük a Criterion() függvénnyel, most végre kell hajtani a megfelelő kereskedelmi műveletet: nyitni, bezárni, módosítani vagy törölni egy függőben levő megbízást. Minden kereskedelmi műveletet különálló felhasználói függvényben helyezünk el: Trade(), Close_All() és Open_Ord(). A csúszó stop megbízás kezelésére a Tral_Stop() felhasználói függvény szolgál

327 Hibafeldolgozó függvény A hibafeldolgozás egy Expert Advisor szerves része. El kell dönteni, hogy hogyan dolgozzuk föl a hibakódot. Néhány esetben elegendő a hibáról egy üzenetet bemutatni: a kért szimbólumnak nem ismert az ára, a kereskedelmi kontextus elfoglalt, stb.. Más esetekben célszerű bizonyos idő után megismételni a kereskedelmi kérést. Az Errors() felhasználói függvényben a hibafeldolgozást a switch operátornak a hibakód alapján kiválasztott variációjában dolgozzuk föl

328 Egy szokásos program szerkezete Egy szokásos program lényeges tulajdonsága a szerkezete, ami lehetővé teszi valamennyi felhasználói és egyéb függvény egyszerű használatát. Az egyszerűség kedvéért a felhasználói függvényeket általában include fájlokban (.mqh) hozzuk létre és a Terminal_directory\experts\include mappában tároljuk. Legtöbbször egy szokásos program tartalmazza mind a három különleges függvényt, amelyek a végrehajtásuk során hívják a szükséges felhasználói függvényeket. A felhasználói függvények a futásuk során hívhatnak egyéb felhasználói függvényeket, mindegyiket a funkcionálisan meghatározott célra. Egy Expert Advisor a legkülönbözőbb tulajdonságú felhasználói függvényeket tartalmazhatja. Néhány függvénynek például az a feladata, hogy figyelemmel kövesse az eseményeket és megfelelő tájékoztatást adjon, egyéb függvényeket arra használunk, hogy a kereskedelmi kéréseket kialakítsák, a harmadik féle függvényeknek a feladata a különböző számítások elvégzése, kereskedési ismertetőjelek meghatározása, a megbízások költségének a meghatározása, stb. Annak eldöntése, hogy milyen függvényeket használjunk a programban az EA céljától függ és attól, hogy a programozó milyen szolgáltatásokkal szándékozik ellátni a felhasználót. A 155. ábrán láthatjuk, hogy egy szokásos kereskedő EA blokkdiagramja a felhasználói függvények készletére épült ábra. Egy szokásos program blokkdiagramja (Expert Advisor). A diagramban a nyilak mutatják a függvények közti kapcsolatokat. Például, az EA-ban a megbízásokat nyilvántartó függvényt hívjuk az init() és start() különleges függvényekből, és szintén hívhatjuk a program bármelyik másik pontjáról. A diagram jobb szélén a csatlakozó nyilak jelzik, hogy lehetősége van a felhasználói függvényeknek másik felhasználói függvény hívására. Például azt a függvényt, ami a kereskedési ismertetőjeleket határoz meg, nem hívjuk a különleges függvényekből csak a kereskedelmi függvényből. Az adatfüggvényt hívjuk a deinit() és start() különleges függvényekből, és ha szükséges másik függvényekből is, például a hibafeldolgozó függvényből, a kereskedelemi függvényből vagy az eseménykövető függvényből. A változók leírását tartalmazó fájlt nem hívhatjuk függvényekből, mert a kód, amit ez a fájl tartalmaz, nem egy függvényleírás, igy nem hívható és nem végrehajtható. Ez a fájl tartalmazza a globális változók leírását, ezért ez is az EA része. Hogy megértsük egy EA különböző részei közötti kölcsönös kapcsolatot, vizsgáljuk meg az include fájlok létrehozását és használatát

329 Az include fájlok használata Ha egy alkalmazási program sok, különféle programsort tartalmaz, ez a hibakeresésben sokszor nehézséget okoz, a programozónak sokszor kell görgetnie a programszöveget, hogy a kódban a különböző helyszíneken a változtatásokat elvégezze. Ilyen esetben célszerű a programot több részre osztani és mindegyik részt egy különálló include fájlban elhelyezni. Az include fájlok bármilyen, a programban használt kódtöredéket tartalmazhatnak. Mindegyik függvényt, amit a programban használnak általában egy különálló include fájlban alakítanak ki. Ha több függvény logikailag összefügg, egy include fájl több felhasználói függvény leírását is tartalmazhatja. A Számlainformáció részben találunk egy példát egy védelmi kódra, ami megakadályozza az üzleti programok jogosulatlan használatát. A check.mq4 Expert Advisorban a programkód megfelelő részét a Check() felhasználói függvényben helyeztük el, aminek a leírását közvetlenül tartalmazza az EA forráskódja. Az usualexpert.mq4 EA ezt a függvényt szintén használja. Azonban ebben az esetben a függvényt a Check.mqh fájlban alakítjuk ki. Check() felhasználói függvény bool Check() A függvény visszaküldi atrue-t, ha megvannak a program használatának a feltételei. Különben visszaküldi FALSE-t. A program használható, ha teljesülnek a következő feltételek: a programot egy demó számlán használjuk; a számlát a SuperBanknál nyitottuk; a felhasználó helyesen állította be a Parol külső változó értékét, valódi számla használata esetén. A Check.mqh include fájl tartalmazza a Check() felhasználói függvény leírását: // // Check.mqh // The program is intended to be used as an example in MQL4 Tutorial. // // The function checking legality of the program used // Inputs: // - global variable 'Parol' // - local constant "SuperBank" // Returned values: // true - if the conditions of use are met // false - if the conditions of use are violated // extern int Parol=12345; // Password to work on a real account // bool Check() // Felhasználói függvény if (IsDemo()==true) // Ha ez egy demó számla.. return(true); //.. akkor nincs korlátozás if (AccountCompany()=="SuperBank") // For corporate clients.. return(true); //..no password is required int Key=AccountNumber()* ; // Calculate the key if (Parol==Key) // If the password is true, then.. return(true); //..allow the user to work on a real account Inform(14); // Message about unauthorized use return(false); // Exit the user-defined function //

330 Könnyen észrevehető, hogy az include fájl neve ugyanaz, mint a benne foglalt felhasználói függvényé. Ezt az MQL4 szabályai nem igénylik. Általában nem szükséges, hogy az include fájl neve ugyanaz legyen, mint a benne lévő függvény neve. Ez még nyilvánvalóbbá válik, ha figyelembe vesszük, hogy egy fájl tartalmazhatja több függvény leírását, vagy egy olyan programrészt, ami egyáltalán nem tartalmaz függvényt. Azonban a gyakorlatban az include fájloknak a benne megvalósított függvény nevét adjuk. Ez jelentősen megkönnyíti a programozó munkáját: miközben az állományneveket használja, tudni fogja, hogy milyen függvények vannak a Terminal_directory\experts\include mappában. Azért, hogy alkalmazni tudjuk az include fájlban lévő programrészletet, az #include utasítást kell használnunk. Az #include utasítás #include <File name> #include "File name" Az #include utasítást a program bármely részén elhelyezhetjük. A preprocesszor fel fogja cserélni az #include <file_name> (vagy #include "file_name") sorokat a hivatkozott fájl tartalmával. A csúcsos zárójel (kacsacsőr) azt jelenti, hogy az include fájl a Terminal_directory\experts\include rendszeresített könyvtárban van (a forrásszöveg mappáját nem vizsgáljuk). Ha az állománynevet idézőjelek közé zárjuk, akkor az include fájlt abban a mappában keresi a preprocesszor, ahol a program forrásszövege található (a Terminal_directory\experts\includerendszeresített könyvtárat nem vizsgálja). Lent látható az usualexpert.mq4 Expert Advisor. Minden include fájlt a program fejrészébe helyeztek. // // usualexpert.mq4 // The code should be used for educational purpose only. // #property copyright "Copyright Book, 2007" #property link " // #include <stdlib.mqh> #include <stderror.mqh> #include <WinUser32.mqh> // #include <Variables.mqh> // Description of variables #include <Check.mqh> // Checking legality of programs used #include <Terminal.mqh> // Order accounting #include <Events.mqh> // Event tracking function #include <Inform.mqh> // Data function #include <Trade.mqh> // Trade function #include <Open_Ord.mqh> // Opening one order of the preset type #include <Close_All.mqh> // Closing all orders of the preset type #include <Tral_Stop.mqh> // StopLoss modification for all orders of the preset type #include <Lot.mqh> // Calculation of the amount of lots #include <Criterion.mqh> // Trading criteria #include <Errors.mqh> // Error processing function // int init() // Special function 'init' Level_old=MarketInfo(Symbol(),MODE_STOPLEVEL );//Min. distance Terminal(); // Order accounting function return; // Exit init() // int start() // Special function 'start' if(check()==false) // If the usage conditions.. return; //..are not met, then exit PlaySound("tick.wav"); // At every tick

331 Terminal(); // Order accounting function Events(); // Information about events Trade(Criterion()); // Trade function Inform(0); // To change the color of objects return; // Exit start() // int deinit() // Special function deinit() Inform(-1); // To delete objects return; // Exit deinit() // A 2-3 blokkban találhatók a program stdlib.mqh, stderror.mqh és WinUser32.mqh standard fájljai használatára utaló #include utasítások. Nem mindig szükséges használni ezeket a fájlokat a programban. Például az stderror.mqh tartalmazza a hibakódok definícióját. Ha hibafeldolgozás nincs a programban (és ezeket az állandókat nem használjuk), nem szükséges tartalmaznia ezt a fájlt a forrásszövegnek. Ugyanakkor a szokásos programok általában tartalmazzák ezeket a fájlokat. A 3-4 blokkban, a program tartalmaz néhány olyan fájlt melyekben a felhasználói függvények leírása található. Használatukat az #include utasítás teszi lehetővé: az #include <Check.mqh> // Checking legality of programs used program forrásszövege tartalmazza a Check() felhasználói függvényt. A programozó az EA (ebben az esetben az usualexpert.mq4) forráskódját a fenti formátumban látja. Azonban a program forrásszövege a fordítás során úgy módosul, hogy mindegyik sort, ami # include utasítást tartalmaz felvált a programkódban az utasításban szereplő fájl. Így, az.ex4 végrehajtható állomány az Expert Advisor teljes kódját tartalmazza, amiben mindegyik #include<fájlnév> (vagy #include "fájlnév") sort a fordító (compiler) lecserélt a megfelelő kódtöredékre. A példa egy olyan fájl, ami egy olyan programtöredéket tartalmaz, ami nem függvényleírás, ez a fájl a Variables.mqh. Ezt a fájlt a programszövegbe a következő sorral illesztjük be: #include <Variables.mqh> // Description of variables ez a fájl tartalmazza azoknak a globális változóknak a leírását, amiket a különböző felhasználói függvények használnak. // // Variables.mqh // The code should be used for educational purpose only. // // Description of global variables extern double Lots = 0.0;// Amount of lots extern int Percent = 0; // Allocated funds percentage extern int StopLoss =100; // StopLoss for new orders (in points) extern int TakeProfit =40; // TakeProfit for new orders (in points) extern int TralingStop=100; // TralingStop for market orders (in points) // int Level_new, // New value of the minimum distance Level_old, // Previous value of the minimum distance Mas_Tip[6]; // Order type array // [] order type: 0=B,1=S,2=BL,3=SL,4=BS,5=SS // double Lots_New, // Amount of lots for new orders Mas_Ord_New[31][9], // Current order array.. Mas_Ord_Old[31][9]; //.. old order array // 1st index = order number in the list // [][0] cannot be detected // [][1] order open price (abs. price value) // [][2] StopLoss of the order (abs. price value) // [][3] TakeProfit of the order (abs. price value)

332 // [][4] order number // [][5] order volume (abs. price value) // [][6] order type 0=B,1=S,2=BL,3=SL,4=BS,5=SS // [][7] Order magic number // [][8] 0/1 the fact of availability of comments // Az MQL4 szabályai szerint, minden változót (beleértve a globális változókat is) az erre a változóra történő első utalás előtt deklarálni kell. Ezért a Variables.mqh fájl bele kell illesztenünk a programba, mégpedig az olyan függvények include fájljai előtt, amik használják azon változók értékeit, amelyeket ebben a fájlban leírtunk. Ezért minden globális változót ebbe a fájlba helyeztünk el. Néhány (ritka) esetben, műszakilag lehetséges deklarálni egy globális változót az olyan include fájlban is ami olyan függvényleírást tartalmaz, amelyben ennek a változónak az értékét először használjuk a programban. Ilyen esetben az include fájlok megfelelő sorrendjét be kell tartani a programban. Vagyis annak a programsornak, amely az arra a fájlra vonatkozó #include utasítást tartalmazza, ahol a globális változók leírása szerepel, meg kell előznie minden olyan programsort ahol ezek a globális változók szerepelnek. Sok esetben még technikailag is lehetetlen ezt megvalósítani. Például ha van két include fájl és mindkettő használ két globális változót, az egyiket az egyik fájlban írjuk le, a másikat pedig a másik fájlban. Ha egy ilyen programot akarunk lefordítani, akkor hibajelzést fogunk kapni, mert azelőtt próbálunk egy változót használni, mielőtt azt deklaráltuk a programban. Ez az oka annak, hogy egy szokásos programban az a gyakorlat, hogy kivételek nélkül minden globális változót egy fájlban írunk le, amit még azelőtt beillesztünk a programba, mielőtt a többi, a felhasználói függvények leírását tartalmazó fájlokat beillesztenénk. Az 1-2 blokkban a Variables.mqh include fájl tartalmazza minden külső változó leírását, az új megbízások lot összegét, a felhasználható szabad margin százalékát, a TakeProfit és StopLoss kért értékét, valamint a TralingStop távolságát. A 2-4 blokkokban az egyéb globális változókat hozzuk létre, amelyek a későbbiekben tárgyalandó felhasználói függvényekhez tartoznak. Az ebben a fejezetben említett include fájlokban lévő felhasználói függvények leírását a következő fejezetekben ismertetjük

333 A megbízások nyilvántartása Már említettük, hogy a programalgoritmusok létrehozására nincsenek szigorú szabályok. Ugyanakkor az algoritmusok döntő többségének a feladata a rendelkezésre álló megbízások figyelembevételével a kereskedési döntések létrehozása. Néhány esetben, mint például megbízások nyitásához nincs szükség arra, hogy már legyen egy másik, létező megbízásunk a kereskedés végrehajtásának a pillanatában. Más esetben egy függőben lévő megbízás elhelyezésének feltétele lehet, hogy a már létező piaci megbízásnak ne legyen beállított stop szintje. Lehet olyan algoritmus is amely két ellentétes függőben lévő megbízás elhelyezésén alapul. A kereskedési taktika megvalósításához a döntéshozatal pillanatában ismernünk kell az aktuális állapotot: milyen piaci és függőben levő megbízások léteznek, és ezeknek milyen jellemzőik vannak? A két lehetséges megoldás közül az egyiket választhatjuk. Az első megoldás szerint, a szükséges programkód részletet (amiben a megbízásokat elemezzük) közvetlenül abban a programban helyezzük el, ahol az elérhető megbízásokra vonatkozó információt felhasználjuk. Ez a megoldás technikailag megvalósítható, de használhatatlan lehet, ha az algoritmusban változtatásokat csinálunk. Ebben az esetben, a programozónak ellenőrizni kell minden olyan programrészt, ahol a megbízásokat elemezzük, és minden programrészben a szükséges változtatásokat végre kell hajtani. Egy másik, hatékonyabb megoldás, ha létrehozunk egy univerzális megbízás elemző függvényt és azt használjuk minden alkalommal, amikor frissíteni akarjuk az aktuális megbízásokkal kapcsolatos információt. Egyfelől ez a megoldás lehetővé teszi, hogy egyszerűsítsük a programkódot. Másfelől lehetővé teszi a programozónak, hogy ezt a kész függvényt használja mikor másik programot kódol. Mielőtt létrehozunk egy megbízás elemző függvény, el kell döntenünk, hogy milyen paramétereket kell elemezni. A legtöbb esetben a következő paraméterek értékeit használjuk a kereskedelmi döntések meghozatalakor: az összes megbízás száma; a különböző típusú megbízások száma (például a Buy megbízások száma, a SellStop megbízások, vagy a BuyLimit megbízások, stb.); minden egyes megbízás minden jellemzője (jegyszám (ticket), StopLoss és TakeProfit szintek, kötés méret, stb.). A fenti információknak elérhetőknek kell lenniük másik függvényekből, hogy ezt az információt azok feldogozhassák. Ezért minden paramétert, ami a megbízásokat jellemez, egy globális tömbben helyezünk el. Összesen három tömböt használunk a megbízások nyilvántartására: a jelenlegi megbízások tömbje a Mas_Ord_New, tartalmazza a jelenlegi piaci és függőben levő megbízások jellemzőivel kapcsolatos információt, a függvény utolsó végrehajtásának pillanatában; az előző megbízások tömbje a Mas_Ord_Old, tartalmazza a piaci és függőben levő megbízások jellemzőivel kapcsolatos információt, a függvény előző végrehajtásának pillanatában; a Mas_Tip tömb elem értékei a különböző típusú megbízások összegét tartalmazzák (az aktuális pillanatban). Az Arrays Mas_Ord_New és Mas_Ord_Old tömbök hasonlóak és azonos dimenziójúak; az köztük a különbség, hogy az első a megbízások aktuális állapotát mutatja, míg a második az ezt megelőző állapotot. Határozzuk meg, hogy a tömbelemek milyen jellemzők értékeit tartalmazzák!

334 4. táblázat. A Mas_Ord_New és Mas_Ord_Old tömbök elemeinek az összefüggése a megbízások jellemzőivel. Nem meghatározott Nyitó ár Stop Loss Take Profit A megbízás (azonosító) száma A megbízás lot mérete A megbízás típusa Magic Number Megjegyzés Indexek A tömb első indexe határozza meg a megbízás sorszámát a tömbben. Az első felismert (függőben levő vagy nyitott) megbízás jellemzői kerülnek az első sorba, a második felismert megbízásé a másodikba stb. A tömb első indexének legnagyobb értéke 31 lehet, így a tömbben legfeljebb 30 megbízással kapcsolatos információt tárolhatunk egyidejűleg. Ha a kereskedési stratégia azt igényli, hogy egyidejűleg harmincnál több megbízással dolgozunk, akkor a tömb deklarálásánál a megfelelő értéket kell adni az első indexnek. (A legtöbb esetben a 30-as érték is lényegesen meghaladja a szükségletet, amit általában 2-től megbízásig terjed. Ebben a példában 30-as értéket használunk, mert azt feltételezzük, hogy a függvényt szokatlan kereskedési stratégiákban is használni fogjuk). A tömb második indexe (az oszlopok) a megbízás jellemzőket tartalmazza. Ha a tömb második dimenziójának indexe egyenlő 1-gyel, akkor az az elem a megbízás nyitó árának az értékét tartalmazza, ha az index 2, akkor az a StopLoss értéke, ha 3, akkor TakeProfit, stb. (lásd a 4. táblázatot). A [0][0] indexű tömbelem értéke a tömbben szereplő megbízások számával egyenlő. Azokat a tömbelemeket, amelyeknek az első vagy a második indexe 0, nem használjuk (a [0][0] indexű elemet kivéve). A 4. táblázatban egy olyan tömböt láthatunk, amely tömb két, egyidejűleg elérhető megbízással kapcsolatos információt tartalmaz. A Mas_Ord_New[0][0] tömbelem értéke 2.0, mert a megbízások száma kettő. A tömb első sorának az elemei egy Sell megbízás jellemzőinek az értékeit tartalmazzák (Mas_Ord_New[1][6] = 1.0, (lásd: Types of Trades). Ezek szerint a Mas_Tip tömb 0 indexű elemének értéke jelenti az összes Buy megbízása számát, az 1indexű elem értéke a Sell megbízások összegét, 2 indexű a BuyLimit megbízásokét, stb. A 4. táblázatban szereplő esetben a Mas_Tip tömb elemeinek az értéke következő lesz: 5. táblázat. A Mas_Tip tömb elemeinek értéke a különböző típusú megbízások számát jelzi. Buy Sell BuyLimit SellLimit BuyStop SellStop Index Value Ebben az esetben, a Mas_Tip tömb elemeinek az értékei a következőket jelentik: Ha a Mas_Tip[1] egyenlő 1 az azt jelenti, hogy egy Sell megbízásunk van; ha Mas_Tip[2] egyenlő 1 akkor egy függőben levő BuyLimit megbízásunk van. A tömb többi elemének értéke nulla - ez azt jelzi, hogy az adott típusú megbízás nincs a kereskedelemben. Ha egyidejűleg több azonos típusú megbízás is elérhető a kereskedelemben, a tömb megfelelő eleménél érték az adott típusú megbízások számával lesz egyenlő. Például, ha három BuyStop megbízásunk van, akkor a Mas_Tip[4] értéke 3 lesz. A Terminal() megbízásokat elemző függvényt javasolt a Terminal.mqh include fájlban kialakítani. A Terminal() felhasználói függvény int Terminal() A függvény a piaci és a függőben levő megbízásokat elemzi. A függvény végrehajtása során változnak a következő globális tömböt értékei:

335 Mas_Ord_New - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a függvény végrehajtásának pillanatában; Mas_Ord_Old - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a függvény előző végrehajtásának pillanatában; Mas_Tip a különböző típusú megbízások száma. A Terminal.mqh include fáj a Terminal() megbízás elemző felhasználói függvény leírását tartalmazza: // // Terminal.mqh // The code should be used for educational purpose only. // // Order accounting function // Global variables: // Mas_Ord_New[31][9] // The latest known orders array // Mas_Ord_Old[31][9] // The preceding (old) orders array // 1st index = order number // [][0] not defined // [][1] order open price (abs. price value) // [][2] StopLoss of the order (abs. price value) // [][3] TakeProfit of the order (abs. price value) // [][4] order number // [][5] order volume in lots (abs. price value) // [][6] order type 0=B,1=S,2=BL,3=SL,4=BS,5=SS // [][7] order magic number // [][8] 0/1 comment availability // Mas_Tip[6] // Array of the amount of orders of all types // [] order type: 0=B,1=S,2=BL,3=SL,4=BS,5=SS // int Terminal() int Qnt=0; // Orders counter // ArrayCopy(Mas_Ord_Old, Mas_Ord_New);// Saves the preceding history Qnt=0; // Zeroize orders counter ArrayInitialize(Mas_Ord_New,0); // Zeroize the array ArrayInitialize(Mas_Tip, 0); // Zeroize the array // for(int i=0; i<orderstotal(); i++) // For market and pending orders if((orderselect(i,select_by_pos)==true) //If there is the next one && (OrderSymbol()==Symbol())) //.. and our currency pair // Qnt++; // Amount of orders Mas_Ord_New[Qnt][1]=OrderOpenPrice(); // Order open price Mas_Ord_New[Qnt][2]=OrderStopLoss(); // SL price Mas_Ord_New[Qnt][3]=OrderTakeProfit(); // TP price Mas_Ord_New[Qnt][4]=OrderTicket(); // Order number Mas_Ord_New[Qnt][5]=OrderLots(); // Amount of lots Mas_Tip[OrderType()]++; // Amount of orders of the type Mas_Ord_New[Qnt][6]=OrderType(); // Order type Mas_Ord_New[Qnt][7]=OrderMagicNumber(); // Magic number if (OrderComment()=="") Mas_Ord_New[Qnt][8]=0; // If there is no comment else Mas_Ord_New[Qnt][8]=1; // If there is a comment // Mas_Ord_New[0][0]=Qnt; // Amount of orders //

336 -- return; // blokkban, a megjegyzésekben megadjuk a globális tömbök elemértékeinek az értelmezését. A globális tömböket a Variables.mqh include fájlban deklaráljuk. A 3-4 blokkban a Mas_Ord_New tömb elem értékeit átmásoljuk a Mas_Ord_Old tömbbe. Így a rendelkezésre álló megbízások előző állapotát eltároljuk és ezeket az adatokat továbbra is használhatjuk a programban. Ezután a Mas_Ord_New és Mas_Tip tömbök elemeinek az értékeit nullára állítjuk be, mielőtt az új adatokat ezekben a tömbökben elhelyeznénk a 4-7 blokkban. A 4-7 blokk tartalmazza azt a ciklust, amiben minden piaci és függőben levő megbízást ellenőrzünk azon a szimbólumon melynek ablakához az EA-t hozzácsatoltuk. A megbízásokat az OrderSelect() függvénnyel választjuk ki az alapértelmezett MODE_TRADES paraméterrel. Az 5-6 blokkban a kiválasztott megbízás minden szükséges jellemzőjét kiszámoljuk és a kapott értékeket elraktározzuk az aktuális megbízások Mas_Ord_New tömbjében. Eközben a különböző típusú megbízások számát is összegezzük és a kapott értékeket adjuk a Mas_Tip tömb megfelelő elemei értékeinek. A ciklus végén valamennyi megbízás számát adjuk a Mas_Ord_New [0][0] elem értékének. A lezárt és törölt megbízásokat nem elemezzük (az OrderSelect() függvényt a MODE_HISTORY paraméterrel nem hajtjuk végre). Az a szabály, hogy a lezárt és törölt megbízásokkal kapcsolatos információt nem használjuk a kereskedő EA-kban. A lezárt és törölt megbízások információit a számlatörténet vizsgálata során használjuk. Ezeket az információkat például a profit változását bemutató diagram létrehozására használhatjuk. Azonban ezek az információk nincsenek befolyással az új kereskedelmi döntésekre. Technikailag ezeket a megbízásokat elemezhetjük, de ez egy olyan különálló feladat, aminek nincs kapcsolata a kereskedelemmel. A megbízásokkal kapcsolatos eseményeket egy programban, azokban a sorokban elemezzük, ahol a fenti tömbökben szereplő adatokat összehasonlítjuk. Például, ha Mas_Ord_Old tömb tartalmazza a jegyszámú függőben levő megbízást, és a Mas_Ord_New tömbben ugyanaz a számú megbízás típusa megváltozott, ez azt jelenti, hogy a függőben levő megbízás piaci megbízássá vált. Szintén elemezni kell a megbízásokat a kereskedés végrehajtásakor (a kereskedés létrejötte utáni állapot figyelembevételével). A Terminal() függvény legelső végrehajtása előtt a Mas_Ord_Old és Mas_Ord_New tömbök üresek, vagyis mindkét tömb minden elemének értéke nulla. Ez azt jelenti, hogy a függvény első végrehajtása után a Mas_Ord_Old tömb a következő sorban: ArrayCopy(Mas_Ord_Old, Mas_Ord_New);// Store the preceding history nulla értékeket kap a Mas_Ord_New tömbből, ami a hamis működéshez vezet az a tömbök értékeit használó függvényeknél. Azért, hogy ezt megakadályozzuk, az usualexpert.mq4 Expert Advisor inicializálása során a Terminal() függvényt egyszer végrehajtjuk, és a start() függvényben történő első végrehajtáskor a Mas_Ord_Old tömb már helyes értékeket tartalmaz

337 Adat függvény Egy szokásos Expert Advisort a gyakorlati munka során felhasználhatunk információs forrásnak is, mert ez a kereskedő számára jó minőségű információs támogatást biztosíthat. A kereskedés alatt a helyzet folyamatosan változik, különféle események zajlanak. Azért hogy megfelelő döntéseket hozzon, a kereskedőnek tájékozottnak kell lennie. Ezt elősegítendő az Expert Advisorban különféle függvényeket használunk. Ezeknek a függvényeknek az a célja, hogy bizonyos tényekről és folyamatokról tájékoztassák a felhasználót. Egy egyszerű Expert Advisorban erre a célra a beépített Comment() függvényt használjuk, ami az előre beállított szöveget a szimbólumablak bal felső sarkában jeleníti meg. Ez az információ megjelenítési módszer nem éppen kényelmes, mert a szövegeket gyakran helyezheti egymásra a charton. Tehát ezt a módszert csak korlátozottan használható rövid üzenetek bemutatására. Ismerjük meg az információ megjelenítésének alapvetően különböző módszerét ahol az egész üzenetet egy különálló ablakban jelenítjük meg, és grafikus objektumokat használunk arra, hogy az üzenet szövegét kialakítsuk. A grafikus objektumok használatának van egy kézzelfogható előnye: a grafikus objektumokat el tudjuk mozdítani (a Comment() függvény szövegeitől eltérően) miközben az üzeneteket létrehozzuk. Egy különálló segédablakot hozunk létre az információk megjelenítésére egy megfelelően beállított egyéni indikátorral. Ennek az indikátornak az egyetlen feladata ennek a segédablaknak a létrehozása, ezért semmilyen számítást nem hajt végre az indikátor, és indikátorvonalakat sem hoz létre. Az Inform.mq4 indikátor kódja a következőképpen néz ki: // // Inform.mq4 // The code should be used for educational purpose only. // #property indicator_separate_window // Separate indicator window // int start() // Special function start() // Azonban a programozó egyéb hozzáadott kódsorokkal módosíthatja az indikátor tulajdonságait. Például a segédablak egy bizonyos részében indikátorvonalakat is elhelyezhet. A fenti példában annak az egyszerű indikátornak a kódját láthatjuk, amellyel azt a segédablakot hozzuk létre, amelyben a grafikus objektumokat meg fogjuk jeleníteni. Az Inform() felhasználói függvény int Inform(int Mess_Number,int Number=0,double Value=0.0) A függvény grafikus objektumokat fog létrehozni az Inform.mq4 indikátor által létrehozott segédablakban. A függvény meghatározza a grafikus objektumok pozícióját az indikátor segédablakban: minden új üzenetet az ablak alsó részében jelenít meg (a legalsó sorban) a kívánt színben, és a korábban mutatott üzeneteket föntebb helyezi (egy sorral följebb). Ha 15 másodpercen belül új üzenetet nem jelenik meg, az ablakban minden üzenet megszürkül (hogy ne tévessze meg a felhasználót). Paraméterek: Mess_Number - üzenetszám, értéke a következő lehet: 0 - üzenet ne legyen megjelenítve, újraindítja az időzítőt; -1 - minden grafikus objektumot, amit létrehozott a függvény, törölni kell; (egy vagy több) - a segédablakban bemutatott üzenetek száma; Number ezt az egész számot néhány üzenetben használjuk; Value - ezt a valós számot néhány üzenetben használjuk. Az Inform() függvény grafikus objektumokat hoz létre, ha az Inform.mqh include fájl segítségével beillesztjük egy szokásos EA-ba: :

338 // // Inform.mqh // The code should be used for educational purpose only. // // Function that displays graphical messages on the screen. // int Inform(int Mess_Number, int Number=0, double Value=0.0) // int Mess_Number // Message number // int Number // Integer to be passed // double Value // Real number to be passed int Win_ind; // Indicator window number string Graf_Text; // Message line color Color_GT; // Color of the message line static int Time_Mess; // Last publication time of the message static int Nom_Mess_Graf; // Graphical messages counter static string Name_Grf_Txt[30]; // Array of graphical message names // Win_ind= WindowFind("inform"); // Searching for indicator window number if (Win_ind<0)return; // If there is no such a window, leave // if (Mess_Number==0) // This happens at every tick if (Time_Mess==0) return; // If it is gray already if (GetTickCount()-Time_Mess>15000)// The color has become updated within 15 sec for(int i=0;i<=29; i++) // Color lines with gray ObjectSet( Name_Grf_Txt[i], OBJPROP_COLOR, Gray); Time_Mess=0; // Flag: All lines are gray WindowRedraw(); // Redrawing objects return; // Exit the function // if (Mess_Number==-1) // This happens at deinit() for(i=0; i<=29; i++) // By object indexes ObjectDelete(Name_Grf_Txt[i]);// Deletion of object return; // Exit the function // Nom_Mess_Graf++; // Graphical messages counter Time_Mess=GetTickCount(); // Last publication time Color_GT=Lime; // switch(mess_number) // Going to message case 1: Graf_Text="Closed order Buy "+ Number; PlaySound("Close_order.wav"); break; case 2: Graf_Text="Closed order Sell "+ Number; PlaySound("Close_order.wav"); break; case 3: Graf_Text="Deleted pending order "+ Number; PlaySound("Close_order.wav"); break; case 4: Graf_Text="Opened order Buy "+ Number; PlaySound("Ok.wav"); break; case 5: Graf_Text="Opened order Sell "+ Number; PlaySound("Ok.wav"); break; case 6: Graf_Text="Placed pending order "+ Number; PlaySound("Ok.wav"); break; case 7: Graf_Text="Order "+Number+" modified into the market one"; PlaySound("Transform.wav"); break; case 8: Graf_Text="Reopened order "+ Number; break;

339 PlaySound("Bulk.wav"); case 9: Graf_Text="Partly closed order "+ Number; PlaySound("Close_order.wav"); break; case 10: Graf_Text="New minimum distance: "+ Number; PlaySound("Inform.wav"); break; case 11: Graf_Text=" Not enough money for "+ DoubleToStr(Value,2) + " lots"; Color_GT=Red; PlaySound("Oops.wav"); break; case 12: Graf_Text="Trying to close order "+ Number; PlaySound("expert.wav"); break; case 13: if (Number>0) Graf_Text="Trying to open order Sell.."; else Graf_Text="Trying to open order Buy.."; PlaySound("expert.wav"); break; case 14: Graf_Text="Invalid password. EA doesn't function."; Color_GT=Red; PlaySound("Oops.wav"); break; case 15: switch(number) // Going to the error number case 2: Graf_Text="Common error."; break; case 129: Graf_Text="Wrong price. "; break; case 135: Graf_Text="Price changed. "; break; case 136: Graf_Text="No prices. Awaiting a new tick.."; break; case 146: Graf_Text="Trading subsystem is busy"; break; case 5 : Graf_Text="Old version of the terminal."; break; case 64: Graf_Text="Account is blocked."; break; case 133: Graf_Text="Trading is prohibited"; break; default: Graf_Text="Occurred error " + Number;//Other errors Color_GT=Red; PlaySound("Error.wav"); break; case 16: Graf_Text="Expert Advisor works only for EURUSD"; Color_GT=Red; PlaySound("Oops.wav"); break; default: Graf_Text="default "+ Mess_Number; Color_GT=Red; PlaySound("Bzrrr.wav"); // ObjectDelete(Name_Grf_Txt[29]); // Deleting 29th (upper) object for(i=29; i>=1; i--) // Cycle for array indexes.. //.. of graphical objects Name_Grf_Txt[i]=Name_Grf_Txt[i-1];// Raising objects: ObjectSet( Name_Grf_Txt[i], OBJPROP_YDISTANCE, 2+15*i); Name_Grf_Txt[0]="Inform_"+Nom_Mess_Graf+"_"+Symbol(); // Object name ObjectCreate (Name_Grf_Txt[0],OBJ_LABEL, Win_ind,0,0);// Creating ObjectSet (Name_Grf_Txt[0],OBJPROP_CORNER, 3 ); // Corner ObjectSet (Name_Grf_Txt[0],OBJPROP_XDISTANCE, 450);// Axis Х ObjectSet (Name_Grf_Txt[0],OBJPROP_YDISTANCE, 2); // Axis Y // Текстовое описание объекта ObjectSetText(Name_Grf_Txt[0],Graf_Text,10,"Courier New",Color_GT); WindowRedraw(); // Redrawing all objects return; // A 2-3 blokkban leírjuk a függvényben használt változókat. A grafikus objektumok neveit a Name_Grf_Txt tömbben tároljuk. A függvényben megvalósított módszer szerint, a program egy új grafikus objektumot hoz létre mindegyik új üzenethez. Az objektumok teljes mennyisége 30 lehet, mindegyik objektum egy egysoros

340 szöveges bejegyzés. Nagy képernyőfelbontás esetén valamennyi sort láthatjuk, amint növekszik a sorok száma minden új objektum létrehozásával. A 3-4 blokkban az "Inform indikátor segédablakának számát keressük meg, ahová az üzenetek fognak kerülni. Ha az indikátor nincs csatolva, a függvény leállítja a működését. Ha az indikátorablak nincs csatolva, az üzeneteket nem lehet megjeleníteni, de ez nem hat az EA egyéb működésére - minden más függvény működni fog a normális módon, kereskedést szintén végre fogja hajtani. A 4-5 blokkban az üzenetek színeit elemezzük. A függvényt a Mess_Number=0 paraméterrel hívja az Expert Advisor minden ticknél, (lásd az usualexpert.mq4 Expert Advisor start() függvényét). Ha minden létező objektum szürke, a függvény befejezi az operációit. Azonban ha a Time_Mess változó értéke nem nulla, az objektumok tulajdonsága megváltozik, mégpedig minden objektum megszürkül. Ha (az 5-6 blokkban) a függvényhívásban a Mess_Number=-1 paramétert találjuk, akkor minden olyan objektumot, amit korábban ez a függvény hozott létre, törölni fogunk. Ez akkor lehet szükséges, amikor az EA-t lekapcsoljuk a szimbólumablakról. Ebben az esetben az ismert szabály szerint mindegyik alkalmazási programnak törölnie kell minden objektumot, amit a végrehajtás alatt létrehozott, (lásd a deinit() függvényt az usualexpert.mq4 Expert Advisorban). Ha a programban a vezérlés a 6-7 blokkba kerül, akkor az a megadott jellemzőkkel létre fog hozni egy grafikus objektumot az indikátor segédablak alsó részén (a legalsó sorban; itt a sor fogalma viszonylagos; valójában a grafikus objektumok elhelyezkedését az előre beállított koordináták határozzák meg). Mindegyik újonnan létrehozott objektumnak egyedi neve van. Az objektumnevek létrehozásához az üzenet történelmi sorszámát használjuk, ezért a 6-7 blokkban egy üzenetszámlálót helyezünk el (a későbbiek során az így kapott Nom_Mess_Graf változó értékét arra fogjuk használni, hogy a 8-9 blokkban kialakítsuk az objektumok egyedi nevét). Itt elemezzük az utolsó üzenet megjelenésének időpontját, és itt állítjuk be az új üzenet színét (zöld). A 7-8 blokkban lévő 'switch' operátor megkeresi a függvényhívásban szereplő Mess_Number paraméternek megfelelő 'case' variációt. Minden 'case' variációhoz tartozik a Graf_Text változónak egy új értéke és ez az érték (string) lesz az új üzenet tartalma. Néhány fontos üzenetnek a színét megváltoztathatjuk például pirosra. Minden üzenet hangjelzéssel párosul, amit a PlaySound() függvény végrehajtásával hozunk létre (lásd: Hang fájlok). Az új grafikus objektum létrehozása és a létezők módosítása a 8-9 blokkban történik. Az objektumok száma korlátozott, ezért a legrégebbi objektumot minden alkalommal törölni kell, mikor egy új üzenet érkezik. Minden más létező objektum elmozdul egy sorral felfelé. Az objektumok azért mozdulnak el, mert megváltoztatjuk a tulajdonságaikat, - a függőleges koordinátákat. Az objektumok vízszintes koordinátái változatlanok maradnak. Miután minden szükséges előkészületet elvégeztünk (minden üzenetet elmozdítottunk egy sorral felfelé), létrehozzuk az új üzenetet a 7-8 blokkban meghatározott egyedi névvel és tulajdonságokkal. A grafikus objektum típusa Text Label. Az ilyen típusú objektumokat a felhasználó önkényesen áthelyezheti a szimbólumablakon belül, függetlenül az üzenet eredeti pozíciójától. Az Inform() függvényt a program bármely pontjáról hívhatjuk, ahol implicite egy szöveges üzenet megjelenítésére van szükség. Hosszú működés során az üzeneteket felhalmozódnak az ablakban. A felhasználó meg tudja nézni a régebbi üzeneteket a segédablak átméretezésével (például az ablak felső szélének felfelé húzásával). Beállítható az ablak olyan méretűre, amikor csak a szükséges számú üzenetsort látjuk (általában három-négy sor ajánlott) ábra. Szimbólumablak. Üzenetek az indikátor segédablakban. Könnyű belátni, hogy a függvénnyel bemutatott üzenetek számát növelhetjük. Ha ki akarjuk terjeszteni a programot, elegendő, ha a 7-8 blokkban a 'switch' operátorhoz újabb case' variációkat adunk hozzá)

341 Eseménykövető függvény Sok esemény történik a kereskedés alatt. A kereskedő néhányat, például a piaci ár-változását vagy indikátorvonalak kereszteződését közvetlenül láthat a szimbólumablakban. Más események, bár szintén fontosak a kereskedő számára nyíltan sehol nem láthatók. Ezeknek az eseményeknek a jelentős részét észlelhetjük és feldolgozhatjuk az MQL4 segítségével. Például a dealing center megváltoztathatja a kereskedelmi feltételeket a fontos hírek előtt, vagy mikor a piac nagyon aktívvá válik. Ilyen esetben a spread vagy a stop megbízások elhelyezésének minimális megengedett távolsága megnövekedhet. Ha ez történik, először is figyelembe kell venni az új kereskedési feltételek meghatározásánál, és másodszor ezekről a változásokról tájékozatni kell a kereskedőt. Ezen feladatok megoldására használhatjuk az eseménykövető függvényt az Expert Advisorban. Az Events() felhasználói függvény int Events() A függvény kiszámítja a stopmegbízások megadásának minimális távolságban, valamint a rendelkezésre álló piaci és függőben levő megbízások listájában bekövetkező változásokat. A függvény végrehajtásához használnunk kell a Terminal() megbízás nyilvántartó függvényt is a programban. A következő globális tömbök értékeit használjuk: Mas_Ord_New - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a Terminal() függvény végrehajtásának pillanatában; Mas_Ord_Old - a rendelkezésre álló megbízások jellemzőit tartalmazó tömb a Terminal() függvény előző végrehajtásának pillanatában; A következő globális változók értékeit használjuk: - Level_new - a minimális távolság értéke; - Level_old - a minimális távolság előző értéke. Az üzeneteket megjelenítésére az Inform() adatfüggvényt fogjuk használni. Ha az Inform() függvény nincs beillesztve az Expert Advisorba, az üzenetek nem fognak megjelenni. Az Events() eseménykövető függvényt az Events.mqh include fájl tartalmazza: // // Events.mqh // The code should be used for educational purpose only. // // Event tracking function. // Global variables: // Level_new The new value of the minimum distance // Level_old The preceding value of the minimum distance // Mas_Ord_New[31][9] The last known array of orders // Mas_Ord_Old[31][9] The old array of orders // int Events() // User-defined function bool Conc_Nom_Ord; // Matching orders in.. //.. the old and the new arrays // Level_new=MarketInfo(Symbol(),MODE_STOPLEVEL );// Last known if (Level_old!=Level_new) // New is not the same as old.. // it means the condition have been changed Level_old=Level_new; // New "old value" Inform(10,Level_new); // Message: new distance // // Searching for lost, type-changed, partly closed and reopened orders for(int old=1;old<=mas_ord_old[0][0];old++)// In the array of old orders // Assuming the.. Conc_Nom_Ord=false; //..orders don't match

342 // for(int new=1;new<=mas_ord_new[0][0];new++)//cycle for the array.. //..of new orders // if (Mas_Ord_Old[old][4]==Mas_Ord_New[new][4])// Matched number // Order type becomes.. if (Mas_Ord_New[new][6]!=Mas_Ord_Old[old][6])//.. different Inform(7,Mas_Ord_New[new][4]);// Message: modified:) Conc_Nom_Ord=true; // The order is found,.. break; //..so exiting.. //.. the internal cycle // // Order number does not match if (Mas_Ord_Old[old][7]>0 && // MagicNumber matches Mas_Ord_Old[old][7]==Mas_Ord_New[new][7])//.. with the old one //it means it is reopened or partly closed // If volumes match,.. if (Mas_Ord_Old[old][5]==Mas_Ord_New[new][5]) Inform(8,Mas_Ord_Old[old][4]);//..it is reopening else // Otherwise, it was.. Inform(9,Mas_Ord_Old[old][4]);//..partly closing Conc_Nom_Ord=true; // The order is found,.. break; //..so exiting.. //.. the internal cycle // if (Conc_Nom_Ord==false) // If we are here,.. //..it means no order found:( if (Mas_Ord_Old[old][6]==0) Inform(1, Mas_Ord_Old[old][4]); // Order Buy closed if (Mas_Ord_Old[old][6]==1) Inform(2, Mas_Ord_Old[old][4]); // Order Sell closed if (Mas_Ord_Old[old][6]> 1) Inform(3, Mas_Ord_Old[old][4]); // Pending order deleted // // Search for new orders for(new=1; new<=mas_ord_new[0][0]; new++)// In the array of new orders if (Mas_Ord_New[new][8]>0) //This one is not new, but reopened continue; //..or partly closed Conc_Nom_Ord=false; // As long as no matches found for(old=1; old<=mas_ord_old[0][0]; old++)// Searching for this order //..in the array of old orders if (Mas_Ord_New[new][4]==Mas_Ord_Old[old][4])//Matched number.. //.. of the order Conc_Nom_Ord=true; // The order is found,.. break; //..so exiting.. //.. the internal cycle if (Conc_Nom_Ord==false) // If no matches found,.. //..the order is new :) if (Mas_Ord_New[new][6]==0) Inform(4, Mas_Ord_New[new][4]); // Order Buy opened if (Mas_Ord_New[new][6]==1) Inform(5, Mas_Ord_New[new][4]); // Order Sell opened if (Mas_Ord_New[new][6]> 1) Inform(6, Mas_Ord_New[new][4]); // Pending order placed // return; // A globális tömböket és változókat, amelyek a függvény végrehajtásához szükségesek, az 1-2 blokkban ismertetjük. A 2-3 blokkban leírt Conc_Nom_Ord helyi változót a kód későbbi részében fogjuk használni a megbízások elemzéséhez

343 A függvény követi a megbízások és stop megbízások elhelyezése minimális távolságának változásait. Ezért, a Level_new minimális távolság értékét mindegyik végrehajtása során meghatározza (3-4 blokk) azután összehasonlítja a Level_old értékével (a függvény előző végrehajtása során meglévő érték). Ha ezeknek a változóknak az értékei nem egyelőek egymásnak, ez azt jelenti, hogy a minimális távolságot a függvény utolsó végrehajtása előtt megváltoztatta a dealing center. Ebben az esetben a minimális távolság új értékét kapja a Level_old változó, és az Inform() függvény végrehajtásával megjelenítjük a megfelelő üzenetet. Általában hasonló módszert használunk más eseményeket észlelésére. Például a spread változása, a kereskedés engedélyezése az adott szimbólumon, (a MODE_TRADEALLOWED azonosító a MarketInfo() függvényben), az aktuális bár befejezése (lásd: 27. feladat), az indikátorvonalak kereszteződése (lásd: 107. ábra ), az előre beállított időpont elérkezése stb. A program néhány esemény jellemző értékét felhasználhatja az EA-ban, más eseményekről tájékoztatja a felhasználót. A 4-10 blokkokban a piaci és függőben levő megbízások állapotát elemezzük. A megbízások állapotának megváltozásával kapcsolatos legtöbb információt megkapja a felhasználó. Az elemzés két szakaszban történik. Az első szakaszban a program azokat a változásokat keresi, amelyek a megszűnt (zárt vagy törölt), típus-módosult vagy részben lezárt (és kisebb méretben visszanyitott) megbízásokkal kapcsolatosak (4-9 blokk). A második szakaszban (9-10 blokk), az új megbízásokat keressük. A 4-9 blokkban az előző ciklusban meglévő megbízásokat elemezzük a Mas_Ord_Old tömb alapján. A külső ciklusban az ismétlések számát a tömbben lévő megbízások száma határozza meg ( Mas_Ord_Old[0][0] tömbelem). Annak megállapítására, hogy a megbízás létezik-e még az adott pillanatban, meg kell keresni azt a Mas_Ord_New tömbben. Ezt a keresést a belső ciklusban hajtjuk végre (6-8 tömb), a belső ciklusban az ismétlések száma egyenlő a Mas_Ord_New tömb megbízásainak számával (Mas_Ord_New[0][0] tömbelem). A későbbiekben a Mas_Ord_Old tömböt előző tömbnek, míg a Mas_Ord_New tömböt új tömbnek fogjuk nevezni. A 6-8 blokkban a program csak azokat a megbízásokat keresi, amelyek jellemzői különbözőek. Például, a 6-7 blokkban, a megbízás jegyszámát ellenőrizzük (lásd a 4.táblázatban a tömbindex és a megbízásjellemzők kapcsolatát). Ha az előző tömb ellenőrzése során egy megbízás jegyszámát megtaláljuk az új tömbben is, ez azt jelenti, hogy ez a megbízás nem lett bezárva (vagy törölve). Szintén meg kell állapítani, hogy megváltozott-e a megbízás típusa. Ha igen, ez azt jelenti, hogy egy függőben levő megbízásból piaci megbízás lett. Ebben az esetben a megfelelő üzenetet jelenítjük meg az Inform() függvénnyel. Attól függetlenül, hogy a megbízás típusa megváltozott-e vagy sem, ezt a megbízást nem fogjuk tovább elemezni: a program kilép a belső ciklusból és elindítja a külső ciklus egy új ismétlését. Ha a program a 6-7 blokkban végrehajtott ellenőrzés során az előző tömbben meglévő jegyszámot az új tömbben nem találja, a vezérlést átadja a 7-8 blokkba. Itt a program megállapítja, hogy az aktuális megbízás MagicNumbere nem nulla (minden nyitott piaci, és elhelyezett függőben lévő megbízásnak az EA adhat egy nem nulla MagicNumbert). Ha a megbízásnak van MagicNumbere és ez a paraméter azonos egy, az új tömbben lévő megbízás MagicNumberjével, ez azt jelenti, hogy ez a megbízás létezik, de a jegyszáma valahogy megváltozott. Két lehetőség van, amikor a jegyszám megváltozhat. 1. lehetőség. A megbízás egy része lett bezárva. Egy piaci megbízást (nem egy függőben levőt!) az MT 4 technológia szerint kétféle képen lehet részlegesen lezárni. Az első módszer az eredeti megbízás teljes lezárása. Ugyanakkor egy kisebb méretű új megbízás nyitása ugyanazzal a nyitó árral és ugyanazokkal a stop szintekkel, mint az eredeti megbízás volt. Ez az új megbízás más azonosítót kap, mint ami az eredeti megbízás azonosítója volt. 2. lehetőség. A megbízást újra kinyitja a dealing center. Valamennyi bróker (a belső könyvelési szabályai miatt) a kereskedési nap végén automatikusan bezár minden megbízást és azonnal kinyitja ugyanazokat a megbízásokat ugyanazzal a típussal és ugyanakkora méretben, de a swap értékét felszámolja. Ez az esemény a swap felszámolásán túl nem hat a kereskedés egyenlegére. Mindegyik újonnan nyitott megbízás a lezárt megbízástól eltérő jegyszámot kap. A fent két eset közti különbség az új megbízások méretében van: az első esetben a lezárt és újranyitott megbízások különbözőek, és a másodikban változatlanok. Ezt az eltérést használjuk a 7-8 blokkban a megbízások újranyitási okainak megkülönböztetésére. Mindkét esetben a megfelelő üzenetet kapjuk (a megbízás részben lezárt vagy a megbízás újra nyitott). Ha a program nem észlelte a megbízás meglétét (6-7 blokk) vagy újranyitását (7-8 blokk) az új megbízások tömbjében, akkor a az előző megbízások tömbjében szereplő megbízást lezárták vagy törölték. Ebben az esetben, a vezérlést a 8-9 blokk kapja meg, ahol a megbízás típusa szerinti üzenet megjelenítése történik. A fenti példában három fajta üzenetet hozhatunk létre: egyet-egyet a Buy és Sell megbízásokhoz, és egyet a függőben levő minden típusú megbízáshoz. Ezt a kódot szükség esetén megváltoztathatjuk (kiterjeszthetjük) - létrehozhatunk mindegyik fajta függőben levő megbízásnak megfelelő üzenetet

344 A későbbiekben a program az új megbízások tömbjét vizsgálja (9-10 blokk). Ezzel újonnan kinyitott és elhelyezett függő megbízásokat észleli. A külső 'for' ciklusban, a program megvizsgál minden megbízást, ami az új megbízások tömbjében található. Az újra nyitott vagy részben zárt megbízások azonosítására a program egy egyszerű jellemzőt használ: a megjegyzéseket. Amikor részben bezár vagy újra kinyit egy megbízást, a szerver a megjegyzés mezőben megadja az eredeti megbízás jegyszámát. A fenti EA nem használ megjegyzéseket, ezért a megjegyzés léte azt jelenti, hogy a vizsgált megbízás nem új. Ha egy megbízás nem tartalmaz megjegyzést, a program ugyanazzal a jegyszámmal keres a megbízások előző tömbjében. Ha a program megtalálja a megbízást a megbízások előző tömbjében, akkor ez azt jelenti, hogy a megbízás nem új, azt régebben nyitották. Mindazonáltal, ha a megbízások új tömbjében található megbízás nem található meg a megbízások előző tömbjében, akkor az a megbízás egy újonnan nyílt piac, vagy újonnan elhelyezett függőben levő megbízás. A 9-10 blokk alsó részében, a program hívja az Inform() függvényt, hogy a megbízás típusának megfelelő üzenetet megjelenítse. A szóban forgó Events() függvény használata a gyakorlatban nagyon informatív. Ha egyszer a programozó kipróbálja ezt a függvényt egy EA-ban, akkor általában használni fogja a további munkájában is. Külön oda kell figyelni az Events() és Terminal() függvények közötti összefüggésre. Ha ezek közül a függvények közül az egyikben változásokat csinálunk (például, globális tömbök más nevet kapnak), akkor a másikban is végre kell hajtani a megfelelő változtatásokat. Ha a kereskedési stratégia megvalósításában használjuk a megbízásban a megjegyzéseket, akkor másként kell meghatározni az újra nyitott megbízásokat (9-10 blokk), mégpedig string függvényeket kell használni a megjegyzések elemzéséhez. A valamennyi esemény szemléltetése az Events() függvényben nagyon megnöveli annak méretét. Például ha teljesen be akarunk mutatni mindent, ami a megbízásokkal történik hozzá kell adni a megbízás jellemzők elemzését - a kért stop szinteket, a függőben levő megbízások kért nyitott árait, a megbízások zárásának módszerét (azt, hogy a megbízást egy szemben lévő megbízás nyitásával zárjuk, vagy attól függetlenül) és a megbízás zárásának/törlésének okát (az ár elérte a kért stop szintet vagy a megbízás a kereskedő kezdeményezésére zárult be, stb.)

345 A megbízás méretét meghatározó függvény A szakmai gyakorlatban a kereskedőnek meg kell adni a lehetőséget, hogy meghatározza új megbízások kötésméretét. Eléggé nehéz létrehozni egy univerzális függvényt erre a célra, mert minden kereskedői stratégia egyedi töke menedzsmentet igényel. Például néhány stratégiában csak egy megbízást használhatunk, míg mások megengedik a meglévőkre való tekintet nélkül új megbízások nyitását. Szintén ismertek stratégiák, amelyek a különböző függőben levő megbízások menedzsmentjén alapulnak, különféle piaci és függőben levő megbízások egyidejű elérhetősége néhány esetben szintén megengedett. Újonnan nyitott megbízások méretének a leggyakoribb számítási módszerei közül az egyik (azon stratégiák esetén, amik csak egy megbízás nyitását engedik meg egy időben) a progresszív befektetési módszer. E szerint a módszer szerint mindegyik új megbízás járulékos költsége arányos a kereskedelem pillanatában rendelkezésre álló szabad marginnal. Ha egy megbízás profittal zárul, az új megbízás megengedett összege növekszik. Ha veszteséggel zárul, a következő megbízás mérete csökken. A lenti példában, a Lot() felhasználói függvény két alternatívát ajánl az új megbízások kötésméretének beállítására: 1. alternatíva: A felhasználó kézzel állítja be az új megbízások lot összegét. 2. alternatíva: A megbízás lot összegét a felhasználó rendelkezésére álló pénz alapján számítjuk ki. Az új megbízásra fordítható összeg a szabad margin meghatározott százaléka. Lot() felhasználói függvény bool Lot() A függvény kiszámítja az új megbízások lot összegét. A függvény végrehajtásnak eredményeképpen a Lots_New globális változó értéke megváltozik: a lot összege. A függvény visszaküldi a TRUE-t, ha a szabad margin elegendő ahhoz, hogy a minimális lot összegével kinyissunk egy megbízást, (azon a szimbólumon, amelynek ablakához az EA-t hozzácsatoltuk). Különben visszaküldi a FALSE-t. A függvény a következő globális változók értékeit használja: Lots a felhasználó által beállított kötésméret lotban meghatározott értéke; Percent - a felhasználó által a szabad margin százalékában meghatározott kötésméret. Az üzenet megjelenítésére az Inform() adatfüggvényt használjuk. Ha az Inform() függvény nem része az EA-nak, az üzenet nem fog megjelenni. A kötésméretet meghatározó Lot() függvényt a Lot.mqh include fájlban valósítjuk meg: // // Lot.mqh // The code should be used for educational purpose only. // // Function calculating the amount of lots. // Global variables: // double Lots_New - the amount of lots for new orders (calculated) // double Lots - the desired amount of lots defined by the user. // int Percent - free margin percentage defined by the user // Returned values: // true - if there is enough money for the minimum volume // false - if there is no enough money for the minimum volume // bool Lot() // User-defined function string Symb =Symbol(); // Symbol double One_Lot=MarketInfo(Symb,MODE_MARGINREQUIRED);//!-lot cost double Min_Lot=MarketInfo(Symb,MODE_MINLOT);// Min. amount of lots double Step =MarketInfo(Symb,MODE_LOTSTEP);//Step in volume changing double Free =AccountFreeMargin(); // Free margin

346 // if (Lots>0) // Volume is explicitly set.. //..check it double Money=Lots*One_Lot; // Order cost if(money<=accountfreemargin()) // Free margin covers it.. Lots_New=Lots; //..accept the set one else // If free margin is not enough.. Lots_New=MathFloor(Free/One_Lot/Step)*Step;// Calculate lots // else // If volume is not preset //..take percentage if (Percent > 100) // Preset, but incorrectly.. Percent=100; //.. then no more than 100 if (Percent==0) // If 0 is preset.. Lots_New=Min_Lot; //..then the min. lot else // Desired amount of lots: Lots_New=MathFloor(Free*Percent/100/One_Lot/Step)*Step;//Calc // if (Lots_New < Min_Lot) // If it is less than allowed.. Lots_New=Min_Lot; //.. then minimum if (Lots_New*One_Lot > AccountFreeMargin()) // It isn't enough even.. //..for the min. lot:( Inform(11,0,Min_Lot); // Message.. return(false); //..and exit return(true); // Exit user-defined function // A függvénynek egyszerű kódja van. Az 1-2 blokkban a globális változókat és azok visszatérési értékeit ismertetjük. A 2-3 blokkban, néhány változó értékét számoljuk ki. Számítások során a következő prioritást érvényesítjük: Ha a felhasználó egy nem nulla összegre állította be a kötésméretet, a szabad margin százalékának beállított értékét nem vesszük figyelembe. Lots és Percent külső változók leírását a Variables.mqh fájl tartalmazza. A 3-4 blokkban, abban az esetben végzünk számításokat, ha a felhasználó egy nem nulla értéket adott a Lots külső változónak. Ekkor a program egy ellenőrzést végez. Ha a szabad margin elegendő a beállított méretű megbízás nyitásához, akkor a Lots_New globális változó megkapja ezt az értéket és a továbbiakban ezt az értéket használjuk a megbízás méretének meghatározására. Ha a szabad margin nem fedezi ezt az összeget, akkor a lehetséges maximális kötésméretet kiszámoljuk, és a továbbiakban azt használjuk (lásd: Matemetikai függvények). Ha a felhasználó nem határozott meg lot méretet, a vezérlés átkerül a 4-5 blokkba. Ezekben a számításokban figyelembe vesszük a szabad margin százalékát, amit a felhasználó a Percent külső változóban beállított. A program egy ellenőrzést végez: Ha ez az érték nagyobb, mint száz (százalék), a 100-as értéket használja a számításokban. Ha a felhasználó a Percent értékét nullának határozta meg, a lot összege a dealing center által meghatározott minimális összeg lesz. Ha köztes értéket ad meg a felhasználó, akkor a program kiszámolja a megfelelő, beállítható értéket. Az 5-6 blokkban a szükséges ellenőrzéseket végezzük. Ha a lot kiszámított összege kevesebb, mint a megengedett minimum (például nulla értéket kapunk a 4-5 blokkban, vagy a felhasználó a Percent a változó értékét túlságosan kicsire választotta), akkor a minimális megengedett értéket fogja kapni a Lots_New változó. Ezután a program ellenőrzi, hogy van-e elegendő szabad pénz a korábban kiszámított lot méretű megbízás nyitásához, (lehet, hogy kevés a számlán levő pénz). Ha a rendelkezésre álló pénz nem elég, a program bemutat egy üzenetet a felhasználónak és kilép a függvényből, a függvény a 'false' értéket küldi vissza. Ha a pénz elegendő a visszatérési érték 'true' lesz

347 A kereskedési ismertetőjeleket meghatározó függvény Bármilyen kereskedő stratégia sikere főleg attól a programrésztől függ, ahol a kereskedési ismertetőjeleket feldolgozzuk. Az a függvény, ami kereskedési jelzéseket szolgáltatja, egy program legfontosabb része és szünet nélkül használni kell. A függvény visszaadott értékeknek kell szolgáltatnia a stratégia szerinti kereskedelmi ismertetőjeleket. Általános esetben a következő ismertetőjeleket határozhatjuk meg: egy megbízás megnyitásának a szempontja; egy megbízás zárásának a szempontja; ismertetőjel egy megbízás részleges zárására; az ellentét megbízások zárásának feltételei; egy megbízás kért stop szintjeinek módosítási szempontjai; egy függőben levő megbízás elhelyezésének a szempontja; egy függőben levő megbízás törlésének a szempontja; egy függőben levő megbízás kért nyitó ára módosításának a szempontja; egy függőben levő megbízás kért stop szintjei módosításának a szempontja. A legtöbb esetben, ha kapunk egy kereskedési ismertetőjelet az kizárja bizonyos másik ismertetőjelek meglétét. Például, ha egy Buy megbízás nyitásának az ismertetőjelei fennállnak egy bizonyos pillanatban, ez azt jelenti, hogy nem lehetnek ugyanakkor Buy záró vagy Sell nyitó ismertetőjelek (lásd: A kereskedés feltételeinek összefüggése). Ugyanakkor, a kereskedési szabályok szerint, a kereskedési stratégiával összhangban, bizonyos féle különböző ismertetőjelek egyidejűleg is létrejöhetnek. Például, egy Sell megbízás befejezésének, és egy függőben levő BuyStop megbízás módosításának szükségessége egyidejűleg is jelentkezhet. A kereskedési stratégia meghatározza a kereskedési ismertetőjeleket meghatározó függvény tartalmára és használatának technológiájára vonatkozó követelményeket. Minden függvény csak egy értéket küldhet vissza. Tehát, ha az Expert Advisorban megvalósítunk egy kereskedő stratégiát, a függvény által visszaadott érték csak egy ismertetőjel meglétét jelezheti, és kölcsönösen kizárja más ismertetőjelek használatát. Azonban, ha a stratégia megengedi különféle ismertetőjelek egyidejű kezelését, azok értékeiket másik függvényekben kell létrehozni és feldolgozni, ezért globális változókat kell használni. A lenti EA-ban megvalósított kereskedő stratégia egyetlen kölcsönösen kizárólagos ismertetőjelet használ. Ezért az itt bemutatott Criterion() függvény által visszaküldött ismertetőjeleket használják a többi függvények. Criterion() felhasználói függvény int Criterion() A függvény a kereskedési ismertetőjeleket határozza meg. A következő értékeket küldheti vissza: 10 jelzés a Buy megbízás nyitása feltételeinek meglétéről; 20 jelzés a Sell megbízás nyitása feltételeinek meglétéről; 11 jelzés a Buy megbízás zárása feltételeinek meglétéről; 21 jelzés a Sell megbízás zárása feltételeinek meglétéről; 0 nincsenek rendelkezésre álló fontos ismertetőjelek; -1 nem az EURUSD szimbólumot használjuk. A függvény a következő külső változók értékeit használja: St_min - a Stochastic Oscillator indikátor alacsonyabb szintje; St_max - a Stochastic Oscillator indikátor magasabb szintje; Open_Level - a MACD indikátor szintje (a megbízás nyitásához); Close_Level - a MACD indikátor szintje (a megbízás zárásához). Az üzenetek megjelenítéséhez használja az Inform() adatfüggvényt. Ha az Inform() függvényt nem tartalmazza az EA, az üzenetek nem fognak megjelenni. A Criterion() kereskedési ismertetőjeleket meghatározó függvény a Criterion.mqh include fájlban megvalósítva:

348 // // Criterion.mqh // The code should be used for educational purpose only. // // Function calculating trading criteria. // Returned values: // 10 - opening Buy // 20 - opening Sell // 11 - closing Buy // 21 - closing Sell // 0 - no important criteria available // -1 - another symbol is used // // External variables: extern int St_min=30; // Minimum stochastic level extern int St_max=70; // Maximum stochastic level extern double Open_Level =5; // MACD level for opening (+/-) extern double Close_Level=4; // MACD level for closing (+/-) // int Criterion() // User-defined function string Sym="EURUSD"; if (Sym!=Symbol()) // If it is a wrong symbol Inform(16); // Messaging.. return(-1); //.. and exiting double M_0, M_1, // Value MAIN at bars 0 and 1 S_0, S_1, // Value SIGNAL at bars 0 and 1 St_M_0, St_M_1, // Value MAIN at bars 0 and 1 St_S_0, St_S_1; // Value SIGNAL at bars 0 and 1 double Opn=Open_Level*Point; // Opening level of MACD (points) double Cls=Close_Level*Point; // Closing level of MACD (points) // // Parameters of technical indicators: M_0=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_MAIN,0); // 0 bar M_1=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_MAIN,1); // 1 bar S_0=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0);//0 bar S_1=iMACD(Sym,PERIOD_H1,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1);//1 bar St_M_0=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_MAIN, 0); St_M_1=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_MAIN, 1); St_S_0=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_SIGNAL,0); St_S_1=iStochastic(Sym,PERIOD_M15,5,3,3,MODE_SMA,0,MODE_SIGNAL,1); // // Calculation of trading criteria if(m_0>s_0 && -M_0>Opn && St_M_0>St_S_0 && St_S_0<St_min) return(10); // Opening Buy if(m_0<s_0 && M_0>Opn && St_M_0<St_S_0 && St_S_0>St_max) return(20); // Opening Sell if(m_0<s_0 && M_0>Cls && St_M_0<St_S_0 && St_S_0>St_max) return(11); // Closing Buy if(m_0>s_0 && -M_0>Cls && St_M_0>St_S_0 && St_S_0>St_min) return(21); // Closing Sell // return(0); // Exit the user-defined function // Az 1-2 blokkban a függvény visszatérési értékeit ismertetjük. A 2-3 blokkban néhány külső változót deklarálunk. Ebben az EA-ban egyedül a Criterion.mqh include fájl használja a szóban forgó (ebben az esetben külső) globális változókat. Az Egy szokásos program szerkezete részben található egy érvelést, hogy kivétel nélkül minden globális változót a különálló Variables.mqh fájlban célszerű deklarálni. Ebben az esetben a külső változókat a Criterion.mqh fájlban deklaráltuk, amit két okkal is megindokolunk: az első hogy demonstráljuk, hogy ez technikailag lehetséges (ez nem mindig lehetséges); a második, hogy bemutassuk hogyan használjuk a külső változókat hibaelhárításnál/ program tesztelésnél. Technikailag lehetséges a külső változókat a Criterion.mqh fájlban deklarálni, mert ezeket a változókat nem használjuk a program semmilyen másik függvényében. A külső változók értékeit a 2-3 blokkban deklaráljuk,

349 hogy a Stochastic Oscillator és MACD indikátorok ezeket az értékeket használják a szóban forgó Criterion() függvényben. Ésszerű lehet a kereskedő ismertetőjeleket meghatározó függvény változóit ideiglenesen külső változóként deklarálni, ezzel megkönnyítve a hibaelhárítást és a változók értékeinek az optimalizálását. E célból más külső változókat adhatunk hozzá a programhoz, például optimalizálhatjuk az indikátorok paramétereit (ebben az esetben az állandók értékeit a MACD-nál 12,26,9 a Stochastic Oscillátornál 5,3,3 értékre állítjuk be). A kódolás végén törölhetjük ezeket a külső változókat a programból és lecserélhetjük őket az optimalizálás alatt meghatározott állandókra. A 3-4 blokkban, létrehozzuk és leírjuk a helyi változókat. Az Expert Advisort az, EURUSD szimbólumon kívánjuk használni, ezt a 3-4 blokkban ellenőrizzük. Ha az EA-t egy másik szimbólum ablakában indítjuk el, a függvény befejezi a működését és -1 értékét (hibás szimbólum) küld vissza. A programban két indikátornak az aktuális és előző báron számolt értékeit számoltak használjuk (4-5 blokk). Általában, amikor a Stochastic Oscillator és MACD indikátorokat használjuk, azt tekintjük kereskedési jelzésnek, amikor a két indikátorvonal találkozik egymással. Ebben az esetben egyidejűleg használunk két indikátort a kereskedési ismertetőjelek meghatározásához. Két indikátor indikátorvonalainak az egyidejű kereszteződésének a valószínűsége meglehetősen alacsony. Sokkal valószínűbb, hogy az egyik vagy másik indikátor kicsit késik. Ha az indikátorvonalak kereszteződése egy rövid időszakon belül történik, akkor elfogadjuk, hogy a két indikátor kialakított egy kereskedési ismertetőjelet. Például, lent megmutatjuk a vételi megbízás kereskedési szempontjának kialakítását (5-6 blokk): if(m_0>s_0 && -M_0>Opn && St_M_0>St_S_0 && St_S_0<St_min) E szerint a sor szerint, a vételi megbízás feltételei fennállnak, ha a következő feltételek teljesülnek: A MACD indikátorban a MAIN (hisztogram) indikátorvonal, a SIGNAL indikátorvonalat alulról fölfelé metszi az Open_Level szintje alatt (157. ábra); Stochastic Oscillator indikátorban az St_min alatti tartományban alulról fölfelé keresztezi MAIN indikátorvonalat a SIGNAL indikátorvonal (158. ábra). Рис Necessary condition of MACD indicator line positions to confirm the importance of trading criteria for opening and closing of orders. 157.ábra. A MACD indikátorvonalak helyzetének elemzése a kereskedési ismertetőjelek meghatározásához. A 157. ábra bal oldali részében a MACD indikátorvonalak azon helyzetét láthatjuk, amikor két ismertetőjelet adnak: Buy megbízás nyitására és Sell zárására. A MAIN indikátorvonal a ös szint alatt van a T1 = t 1 - t 0 időintervallumban. Ha a Stochastic Oscillator kereskedelmi jelzése ezen az időintervallumon belül történik, akkor azt Buy megbízás nyitására vonatkozó kereskedelmi jelzésként értékeljük. A MAIN indikátorvonal a T2 = t 2 - t intervallumban a es szint alatt van. Ha Stochastic Oscillator jelei megerősítik ezt a pozíciót, akkor az a Sell megbízás befejezésének szükségességét jelzi

350 Jegyezd meg, formálisan a T1időintervallumon belül két ismertetőjelet kapunk (ha azt a Stochastic Oscillator megerősítette). A Criterion() függvény, mint már említettük, csak egy értéket küldhet vissza, egyszerre csak egyféle kereskedelmi jelzést adhat. Ez alatt az időszak alatt szükségessé válik, hogy kiválasszuk a két ismertetőjel közül az egyiket. Ezt a feladatot előre, a programozás alatt oldjuk meg a kereskedési stratégia által meghatározott prioritások alapján. Ebben az esetben (a szóban forgó kereskedési stratégia szerint), a Buy megbízás nyitásának a prioritása magasabb, mint a Sell megbízás befejezésének. Ezért, az 5-6 blokkban, azt a programsort, amiben a Buy megbízás megnyitásának a szempontjait értékeljük, fentebb helyezzük el. Ha T1 időszaka alatt a Stochastic Oscillatortól megerősítést kapunk (157. ábra), a függvény 10-es értéket küld vissza, amit ehhez az ismertetőjelhez társítottunk. A t1- t2 időszakon belül a függvény 21-et fog visszaküldeni, ami a Sell megbízás zárásának ismertetőjele. Ugyanakkor a kereskedelmi függvény végrehajtása során kialakítjuk a szükséges kereskedelmi kéréseket. A Buy nyitását jelző ismertetőjel megjelenésekor először is minden elérhető Sell megbízás bezárására irányuló kereskedelmi kéréseket fogunk kialakítani. Amint ilyen megbízásaink már nincsenek, egy Buy megbízás megnyitását fogjuk kérni. Illetőleg, amikor a Sell megbízás zárásának a feltételei érkeznek el, akkor minden egyes Sell megbízást bezárunk (Buy megbízás nyitása nélkül) (lásd: Kereskedelmi függvények). Azokat a feltételek, amelyek alapján a Stochastic Oscillator megerősíti a kereskedelmi jelzést, láthatjuk a 158. ábrán ábra. A Stochastic Oscillator indikátorvonalak elhelyezkedése megerősíti a vételi vagy eladási ismertetőjeleket. Az 5-6 blokkban elhelyezett programkód szerint, a Buy megnyitásának és Sell zárásának a feltételei bekövetkeznek, ha a MAIN indikátorvonalat a Stochastic Oscillatorban alulról keresztezi a SIGNAL vonal, miközben a MAIN vonal a St_min minimális szint alatt van. A 158. ábrán ezek a feltételek a T 2 időszakon belül teljesülnek. A fenti feltételek tükörképe a Sell nyitásának és a Buy zárásának feltétele (a 158. ábra jobb oldalán). Ha nincs ismertetőjel, a függvény 0-t küld vissza. Más kereskedelmi kéréseket végrehajthatunk az alatt az időszak alatt, például: módosíthatjuk a stop szinteket. Megfigyelhető, hogy a szóban forgó kereskedési stratégia a MACD indikátort 1 órás időkereten használja, míg a Stochastic Oscillatort 15 perces idő kereten. Az idő keretet a tesztelés során megváltoztathatjuk, hogy optimalizáljuk a stratégiát. Azonban a tesztelés után Criterion() függvény végső kódjában azokat az állandó paramétereket kell megadni, amelyekkel (külső változóként) a legjobb tesztelési eredményt kaptuk. Az EA-t csak a létrehozásakor megadott paraméterekkel lehet használni. A fenti példában (PERIOD_H1 és PERIOD_M15 értékeivel, amelyeket az indikátorokban beállítottunk), az EA ezeket a paramétereket fogja használni az EA szimbólumablakának aktuális időkeretétől függetlenül. Azokat a kereskedési ismertetőjeleket, amelyeket az adott EA ban alkalmaztunk, csak szemléltetésre használtuk, és nem tekinthető egy valódi számlán történő kereskedési útmutatónak

351 Kereskedelmi függvények Egy szokásos Expert Advisor sok kereskedelmi függvényt tartalmaz. Két kategóriába sorolhatjuk őketvezérlő függvényekre és végrehajtó függvényekre. A legtöbb esetben csak egy vezérlő függvényt és különféle végrehajtó függvényeket használnak egy EA-ban. Egy szokásos EA-ban a kereskedő stratégiát két függvényben valósítják meg - egy kereskedési ismertetőjeleket meghatározó függvényben és a kereskedést megvalósító függvényben. A kereskedői stratégia a program többi részére nem lehet hatással. A kereskedést végrehajtó és a kereskedési ismertetőjeleket szolgáltató függvényeket össze kell hangolni egymással az átadott paraméterek értékei alapján. Mindegyik kereskedelmi függvény meghatározott feladatokat láthat el. A kereskedési stratégia alapján a kereskedelmi függvény a következő feladatokat láthatja el az EA-ban: kinyit egy előre beállított típusú megbízását; bezár egy előre beállított típusú megbízását; részlegesen bezár egy előre beállított típusú megbízást; bezár minden előre beállított típusú megbízását; bezár két ellentétes, előre beállított volumenű megbízást; bezár minden piaci megbízást; módosítja az előre beállított típusú megbízás stop szintjét; elhelyezi az előre beállított típusú függőben levő megbízást; törli az előre beállított típusú függőben levő megbízást; töröl minden előre beállított típusú függőben levő megbízást; minden függőben levő megbízás törlése; az előre beállított típusú függőben levő megbízás módosítása. Egy általános kereskedelemi kód egy szokásos Expert Advisorban a következőkből áll: A (stratégia alapján) létrehozott kereskedési ismertetőjelek megjelenésekor a kereskedelmet megvalósító függvény (szintén a stratégia alapján) hívja a kereskedési kérést ténylegesen kialakító függvényeket. Trade() felhasználói vezérlő függvény int Trade( int Trad_Oper ) Ez az az alapvető függvény, ami megvalósítja a kereskedési stratégiát. A Trad_Oper paraméter a következő értékeket veheti fel a kereskedési ismertetőjeleknek megfelelően: 10 jelzés a Buy megbízás nyitása feltételeinek meglétéről; 20 jelzés a Sell megbízás nyitása feltételeinek meglétéről; 11 jelzés a Buy megbízás zárása feltételeinek meglétéről; 21 jelzés a Sell megbízás zárása feltételeinek meglétéről; 0 nincsenek rendelkezésre álló fontos ismertetőjelek; -1 nem az EURUSD szimbólumot használjuk. A függvény végrehajtása a következő kereskedelmi függvényeket igényli: Close_All() a függvény bezár minden előre beállított típusú megbízást; Open_Ord() a függvény kinyit egy előre beállított típusú megbízást; Tral_Stop() a függvény módosítja az előre beállított típusú megbízás StopLoss értékét; Lot() a függvény meghatározza az új megbízás lot összegét. A Trade() kereskedelmet vezérlő függvény a Trade.mqh incklude fájlban megvalósítva: // // Trade.mqh // The code should be used for educational purpose only. // // Trade function. // int Trade(int Trad_Oper) // User-defined function

352 // Trad_Oper - trade operation type: // 10 - opening Buy // 20 - opening Sell // 11 - closing Buy // 21 - closing Sell // 0 - no important criteria available // -1 - another symbol is used switch(trad_oper) // case 10: // Trading criterion = Buy Close_All(1); // Close all Sell if (Lot()==false) // Not enough money for min. return; // Exit the user-defined function Open_Ord(0); // Open Buy return; // Having traded, leave // case 11: // Trading criterion = closing Buy Close_All(0); // Close all Buy return; // Having traded, leave // case 20: // Trading criterion = Sell Close_All(0); // Close all Buy if (Lot()==false) return; // Exit the user-defined function Open_Ord(1); // Open Sell return; // Having traded, leave // case 21: // Trading criterion = closing Sell Close_All(1); // Close all Sell return; // Having traded, leave // case 0: // Retaining opened positions Tral_Stop(0); // Trailing stop Buy Tral_Stop(1); // Trailing stop Sell return; // Having traded, leave // // A Trade() kereskedelemvezérlő függvényt az usualexpert.mq4 Expert Advisor start()különleges függvényéből hívjuk. Az Criterion() kereskedési ismertetőjeleket meghatározó függvény visszatérési értéke lesz a Trade() függvény átadott paramétere. A Trade() függvény 1-2 blokkjában ismertetjük a kereskedési ismertetőjelek paramétereinek a jelentését, amelyeket a kereskedési stratégia megvalósítása során alkalmazunk. A függvényben a swich operátort használjuk arra (2-7 blokk), hogy kiválassza a kereskedési ismertetőjelek által meghatározott függvénycsoportot, amellyel az ismertetőjeleknek megfelelő módon kereskedhetünk. Az EA a kereskedési stratégia szerint megbízásokat zár be és nyit ki. A függőben levő megbízásokat nem használja ez a stratégia. A kereskedési ismertetőjeleket meghatározó függvény részben leírtuk azt, hogy a kereskedési ismertetőjeleknek megfelelően a program többféle kereskedelmi kérést is kialakíthat. Így, a vételt jelző ismertetőjel esetében (a Trad_Oper változó értéke egyenlő 10-zel), a switch operátor végrehajtása során a vezérlést a 10-es variációnak megfelelő (2-3 blokk) függvénycsoport kapja. Ebben az esetben a program először a Close_All(1) függvényt hívja. Ennek a függvénynek a végrehajtása minden Sell megbízás zárásával végződik, amit az EURUSD szimbólumon nyitottak. Miután minden Sell megbízás le lett zárva, ellenőrizzük a rendelkezésre álló pénzt, hogy elegendő-e a következő megbízáshoz. Ezért hívjuk a Lot() felhasználói függvényt (lásd: A megbízás méretét meghatározó függvény). Ha a függvény visszatérési értéke false', ez azt jelenti, hogy a rendelkezésre álló pénz nem elég arra sem, hogy a Buy megbízást megnyissuk a minimum megengedett összeggel. Ebben az esetben a Trade() függvény befejeződik. Ha van elegendő pénz, hívjuk az Open_Ord(0) kereskedelmi függvényt, ami nyit egy Buy megbízást, a Lot() függvény végrehajtása során kiszámított lot összegével. A leírt intézkedéscsomag képviseli az Expert Advisor válaszát a piaci helyzetre (az adott kereskedelmi ismertetőjel szerint)

353 Ha az ismertetőjel a Buy megbízások zárásának a szükségességét jelzik, akkor a 'case 11' variáció teljesül és a vezérlés a 3-4 blokkba kerül. Ebben az esetben, csak a Close_All(0) függvényt hívjuk, hogy bezárja az összes rendelkezésre álló Buy típusú megbízást. A 4-6 blokk felépítése hasonló a 2-4 blokkhoz, ahová a 'case 20' variáció teljesülése esetén kerül a vezérlés, a 'case 21' variáció teljesülése pedig az összes Sell megbízás zárásához vezet. Vegyük figyelembe, hogy minden, a Trade() trade függvényből hívott kereskedelmi függvényt, amelyek a kereskedelmi kéréseket kialakítják, akkor hajtunk végre amikor az új tick következtében az EA start() függvényének végrehajtását az ügyfélterminál elindítja. A Trade() függvény kódja úgy van létrehozva, hogy a vezérlést addig nem adja vissza a start() függvénynek (és az ügyfélterminálnak) amíg minden kereskedelmi kérést megvalósító függvényt végre nem hajt. Ennek következtében a kereskedési ismertetőjeleknek megfelelően, minden kereskedelmi kérést hiánytalanul végrehajt az EA. Kivétel lehet az az eset, ha kritikus hiba történik a kereskedés végrehajtása során (lásd: Hibafeldolgozó függvény). Ha fontos kereskedő ismertetőjelek nincsenek, a Criterion() függvény végrehajtás után a (Trad_Oper változó egyenlő 0-val) 'case 0' variáció teljesül és a Tral_Stop() függvény két egymás utáni hívására kerül sor, hogy a különböző típusú megbízások kért stop értékeit módosítsuk. Az EA-ban megvalósított kereskedési stratégia csak egy megbízás jelenlétét engedi meg, ebből a szempontból a Tral_Stop(0) és Tral_Stop(1) hívásának sorrendje közömbös. Ebben az esetben ez tetszőleges sorrend lehet. Ha a Criterion() -1 értéket küldött vissza, ez azt jelenti, hogy az EA-t egy olyan szimbólumablakhoz erősítettük hozzá, ami nem EURUSD. Ebben az esetben a Trade() függvény nem hív végrehajtó kereskedelemi függvényeket, hanem visszaküldi a vezérlést a különleges start() függvénybe. Close_All() kereskedelmet végrehajtó felhasználói függvény int Close_All( int Tip) A függvény bezárja az adott típus minden megbízását. A Tip paraméter a következő értékeket veheti fel, a bezárandó megbízás típusa szerint: 0 - Buy megbízás zárása; 1 - Sell megbízás zárása. Ezen függvény végrehajtásához a következő függvények megléte is szükséges: Terminal() megbízásokat nyilvántartó függvény, Events() eseménykövető függvény és Errors() hibafeldolgozó függvény. Ha üzeneteket is meg akarunk jeleníteni, akkor az Inform() függvényt is használnunk kell. Ha az EA nem tartalmazza az Inform() függvényt akkor az üzenetek nem fognak megjelenni. A következő globális tömbök értékeit használjuk: a jelenlegi megbízások tömbje a Mas_Ord_New, tartalmazza a jelenlegi piaci és függőben levő megbízások jellemzőivel kapcsolatos információt, a függvény utolsó végrehajtásának pillanatában; a Mas_Tip tömb elem értékei a különböző típusú megbízások összegét tartalmazzák (az aktuális pillanatban). A Close_All() végrehajtó kereskedelmi függvényt a Close_All.mqh include fájlban megvalósítva: // // Close_All.mqh // The code should be used for educational purpose only. // // Function closing all market orders of the given type // Global variables: // Mas_Ord_New Last known order array // Mas_Tip Order type array // int Close_All(int Tip) // User-defined function // int Tip // Order type int Ticket=0; // Order ticket double Lot=0; // Amount of closed lots double Price_Cls; // Order close price //

354 while(mas_tip[tip]>0) // As long as the orders of the.. //.. given type are available for(int i=1; i<=mas_ord_new[0][0]; i++)// Cycle for live orders if(mas_ord_new[i][6]==tip && // Among the orders of our type Mas_Ord_New[i][5]>Lot) //.. select the most expensive one // This one was found at earliest. Lot=Mas_Ord_New[i][5]; // The largest amount of lots found Ticket=Mas_Ord_New[i][4]; // Its order ticket is that if (Tip==0) Price_Cls=Bid; // For orders Buy if (Tip==1) Price_Cls=Ask; // For orders Sell Inform(12,Ticket); // Message about an attempt to close bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// Close order!:) // if (Ans==false) // Failed :( // Check for errors: if(errors(getlasterror())==false)// If the error is critical, return; //.. then leave. // Terminal(); // Order accounting function Events(); // Event tracking return; // Exit the user-defined function // Az 1-2 blokkban, a globális változókat írjuk le. A 2-3 blokkban létrehozzuk és leírjuk a lokális változókat. A Mas_Tip[Tip]>0 feltétel a 'while' ciklusoperátor fejlécében (3-6 blokk) azt jelenti, hogy függvény addig fogja magánál tartani a vezérlést, amíg az operátor teljesíti a feladatát, vagyis amíg az adott típus minden megbízását be nem zárja. A Mas_Tip[Tip] globális tömb eleme tartalmazza azt az értéket, ami egyenlő az adott típusú megbízások számával. Például, ha a Close_All() függvényt(1) átadott paraméterrel hívjuk, ez azt jelenti, hogy a függvénynek be kell zárnia minden Sell megbízást (lásd: A megbízások típusai). Ebben az esetben, a Mas_Tip[1] tömbelem értéke egyenlő lesz valamennyi elérhető Sell megbízással (a Terminal() függvény utolsó végrehajtásának a pillanatában. Így, a 'while' ciklusoperátort annyiszor fogjuk végrehajtani, amennyi Sell megbízás elérhető. Ha a kereskedő nem avatkozik be az EA működésébe (és nem ad kézzel megbízásokat), akkor csak egy darab, egyik vagy másik típusú megbízás lehet elérhető. Azonban, ha a kereskedő kézzel további megbízásokat nyitott, előfordulhat, hogy a Close_All() függvénynek több megbízást kell lezárnia. A megbízások lezárásának legjobb sorrendje az, ha a legnagyobb megbízást zárjuk be legelőször. Például, ha három Sell megbízásunk van a Close_All() függvény végrehajtása pillanatában, az elsőnek nyitott 5 lot, a másodiknak nyitott 1lot és a harmadiknak nyitott 4 lot méretű, akkor a fenti érvelés szerint a megbízásokat a következő sorrendben kell bezárni: először az 5 lotost, azután a 4 lotost, és végül az 1 lotost. Vegyük figyelembe, hogy lot összege az egyetlen ismertetőjel, ami alapján a megbízások bezárásának a sorrendjét meghatároztuk. A megbízás nyereségét/veszteségét, nyitó árát és egyéb paramétereit (kért stop szintet, a nyitás idejét és a bezárás okát stb.) nem vesszük figyelembe. Ha bizonyos típusú megbízásokat egyszerre kell bezárni, fontos, hogy a bezárást a legnagyobb megbízással kezdjük, és azután következzenek a kisebbek. A megbízások zárásának fenti sorrendjének betartására a 3-4 blokkban a 'for' ciklust használjuk, amiben a legnagyobb méretű megbízást választjuk ki az adott típusú megbízások közül. Ezt a megbízást (a legnagyobbat) Mas_Ord_New globális tömb értékeinek az elemzése alapján keressük meg, ahol megtalálható minden, a kereskedelemben rendelkezésre álló megbízással kapcsolatos információ. Miután ennek a megbízásnak a jegyszámát megtaláltuk, a kettős árnak a megbízás típusa szerinti értékével kiszámoljuk a kért záró árat. Ha Buy típusú megbízást kell lezárni, akkor a Bid értéket használjuk. Ha Sell megbízást, akkor az Ask értéket

355 A kereskedelmi kérés kialakítása előtt közvetlenül megjelenítjük, a bezárási kísérlettel kapcsolatos információt. Ezért a program hívja az Inform()függvényt. A megbízás befejezése iránti kereskedelmi kérést a következő sorban alakítjuk ki: bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// Close order!:) A kiszámított értékeket paraméterekként használjuk: Ticket - jegyszám, Lot - kötésméret, Price_Cls a kért záró ár, 2 - csúszás. A 4-5 blokkban a kereskedelem eredményeit elemezzük. Ha az OrderClose() függvény visszatérési értéke 'true', ez azt jelenti, a kérés végrehajtása sikeres és a megbízás le lett zárva. Ebben az esetben a vezérlés átkerül az 5-6 blokkba, ahol az az aktuális pillanatban rendelkezésre álló megbízásokkal kapcsolatos információt frissítik. A Terminal() és Events() függvények végrehajtása után a 'while' ciklus aktuális ismétlése véget ért (az elérhető megbízások száma a ciklus végrehajtása alatt megváltozhat, ezért a megbízásokat nyilvántartó Terminal() függvény végrehajtása kötelező a 'while' ciklus mindegyik ismétlésénél). Ha az adott típusú megbízások még mindig elérhetőek, ezek a 'while' ciklus következő ismétlésénél lesznek lezárva, a Mas_Ord_New és Mas_Tip tömböknek a Terminal() függvény újbóli végrehajtása során kapott értékeit fogjuk a következő megbízás lezárásakor paraméterekként felhasználni. Ha a kereskedelmi kérés végrehajtása azzal végződik, hogy az OrderClose() függvény 'false' értéket küld vissza, ez azt jelenti, hogy a megbízás nem lett lezárva. Azért, hogy a sikertelenség okát megtudjuk, a program elemzi az utolsó hiba okát. Ezért hívni fogja az Errors() fügvényt (lásd: Hebafeldolgozó függvény). Ha ennek a függvénynek a végrehajtásánál a program azt érzékeli, hogy a hiba kritikus (például a kereskedés tiltva van), a Close_All() függvény befejezi a működését és visszaküldi a vezérlést a kereskedést felügyelő Trade() függvénybe, amely azt továbbadja a különleges start() függvénynek és az EA végrehajtása befejeződik. A következő ticknél a terminál ismét el fogja indítani a start() függvény végrehajtását. Ha a záró ismertetőjel abban a pillanatban még fennáll, akkor a Close_All() függvény hívása újra megtörténik. Open_Ord() kereskedelmet végrehajtó felhasználói függvény int Open_Ord ( int Tip) A függvény nyit egy adott típusú megbízást. A Tip paraméternek a következő értékei lehetnek, amely jelzi a nyitandó megbízás típusát: 0 - Buy típusú megbízás nyitása; 1 - Sell típusú megbízás nyitása. Ezen függvény végrehajtásához a következő függvények megléte is szükséges: Terminal() megbízásokat nyilvántartó függvény, Events() eseménykövető függvény és Errors() hibafeldolgozó függvény. Ha üzeneteket is meg akarjuk jeleníteni, akkor az Inform() függvényt is használnunk kell. Ha az EA nem tartalmazza az Inform() függvényt akkor az üzenetek nem fognak megjelenni. A következő globális változók értékeit használjuk: a Mas_Tip tömb elem értékei a különböző típusú megbízások összegét tartalmazzák (a Terminal() függvény végrehajtásának pillanatában). StopLoss - StopLoss értéke (pontokban); TakeProfit - TakeProfit értéke (pontokban). A Open_Ord() végrehajtó kereskedelmi függvény az Open_Ord.mqh include fájlban megvalósítva: // // Open_Ord.mqh // The code should be used for educational purpose only. // // Function opening one market order of the given type // Global variables: // int Mas_Tip Order type array // int StopLoss The value of StopLoss (amount of points) // int TakeProfit The value of TakeProfit (amount of points) //

356 int Open_Ord(int Tip) int Ticket, // Order ticket MN; // MagicNumber double SL, // StopLoss (as related to the price) TP; // TakeProf (as related to the price) // while(mas_tip[tip]==0) // Until they.. //.. succeed if (StopLoss<Level_new) // If it is less than allowed.. StopLoss=Level_new; //.. then the allowed one if (TakeProfit<Level_new) // If it is less than allowed.. TakeProfit=Level_new; //..then the allowed one MN=TimeCurrent(); // Simple MagicNumber Inform(13,Tip); // Message about an attempt to open if (Tip==0) // Let's open a Buy SL=Bid - StopLoss* Point; // StopLoss (price) TP=Bid + TakeProfit*Point; // TakeProfit (price) Ticket=OrderSend(Symbol(),0,Lots_New,Ask,2,SL,TP,"",MN); if (Tip==1) // Let's open a Sell SL=Ask + StopLoss* Point; // StopLoss (price) TP=Ask - TakeProfit*Point; // TakeProfit (price) Ticket=OrderSend(Symbol(),1,Lots_New,Bid,2,SL,TP,"",MN); // if (Ticket<0) // Failed :( // Check for errors: if(errors(getlasterror())==false)// If the error is critical, return; //.. then leave. Terminal(); // Order accounting function Events(); // Event tracking // return; // Exit the user-defined function // Az Open_Ord() függvény 1-3 blokkjában azokat a globális változókat ismertetjük, amelyek értékeit ebben a függvényben használjuk, valamint létrehozzuk a lokális változókat. A függvény alapvető kódja a 'while' ciklusoperátorban összpontosul (3-5 blokk) azt annyiszor hajtjuk végre, amíg nem jön létre Tip típusú megbízás. A kereskedési stratégia olyan megbízások nyitását írja elő, ahol a megbízás nyitásával egy időben a stop szinteket is beállítjuk. Általános esetben a kereskedő a piaci árhoz viszonyítva helyezi el a stop szinteket, azonban ezek a stop szintek nem minden esetben tartják be a dealing center által meghatározott minimális távolság követelményét. Ezért, a megbízás nyitása előtt ezt ellenőrizni kell: Ha az utoljára ismert minimális távolság (Level_new) nagyobb, mint a StopLoss vagy TakeProfit külső változók értéke, e változók értékét Level_new-nak állítjuk be. Minden megbízás nyitására irányuló kereskedelmi kérés tartalmazza az egyedi MagicNumbert ami megegyezik az aktuális szerveridővel. Egy EA végrehajtásának következtében egy szimbólumon csak egy megbízás lehet nyitva (vagy egy függőben levő megbízás lehet) egy időben. Egy megbízás nyitása előtt végrehajtjuk az Inform() függvényt, amely bemutat egy tájékoztató üzenetet a megbízás nyitásának kísérletéről. A megbízás típusa szerint, az egyik 'if' operátortörzset hajtjuk végre. Például, ha a Tip paraméter értéke egyenlő 0-val, ez azt jelenti, hogy egy Buy megbízást kell nyitni. Ebben az esetben a StopLoss és TakeProfit értékeit kiszámítjuk, a Buy megbízástípusnak megfelelően, azután a vezérlés a következő sorba kerül, Ticket=OrderSend(Symbol(),0,Lots_New,Ask,2,SL,TP,"",MN); ahol létrehozzuk a Buy megbízás kialakulására irányuló kereskedelmi kérést. Hasonló számításokat végzünk, ha a Tip paraméter értéke 1, vagyis egy Sell megbízást kell nyitni

357 A hibákat a többi felhasználói kereskedelmet végrehajtó függvényhez hasonlóan dolgozzuk fel. Ha a kereskedelmi kérés végrehajtása sikerül, a függvény befejezi a működését (nincs a 'while' ciklusnak következő ismétlése mert a Terminal() függvény végrehajtása után a Mas_Tip[Tip] elemének az értéke egyenlő lesz 1-gyel). Azonban, ha a kereskedelmi kérés végrehajtása nem sikerül, a hibákat elemezzük (4-5 blokk). Ebben az esetben hívjuk az Errors() hibafeldolgozó függvényt. Ha ennek a visszatérési értéke 'false' (a hiba kritikus), az Open_Ord() függvény végrehajtása véget ér, és a vezérlés egymás után átkerül a Trade() kereskedelmi vezérlő függvénybe, onnan a start() függvénybe majd azután az ügyfélterminálhoz. Mindazonáltal, ha a hiba kiküszöbölhető, az adatokat frissítjük a Terminal() függvényben és a vezérlést ismét megkapja a 'while' ciklus következő ismétlésére, amíg a megbízás nyitása sikerrel nem jár. Így, az Open_Ord() függvény mindaddig magánál tartja a vezérlést, amíg a megbízás nyitása nem sikerül, vagy kritikus hiba nem történik. Tral_Stop() kereskedelmet végrehajtó felhasználói függvény int Tral_Stop ( int Tip) A függvény módosít minden adott típusú megbízást. A Tip paraméter következő értéke képviseli a módosítandó megbízás típusát: 0 - a Buy típusú megbízásokat módosítjuk; 1 - a Sell típusú megbízásokat módosítjuk. Ezen függvény végrehajtásához a következő függvények megléte is szükséges: Terminal() megbízásokat nyilvántartó függvény, Events() eseménykövető függvény és Errors() hibafeldolgozó függvény. Ha üzeneteket is meg akarjuk jeleníteni, akkor az Inform() függvényt is használnunk kell. Ha az EA nem tartalmazza az Inform() függvényt akkor az üzenetek nem fognak megjelenni. A következő globális változók értékeit használjuk: - a jelenlegi megbízások tömbje a Mas_Ord_New, tartalmazza a jelenlegi piaci és függőben levő megbízások jellemzőivel kapcsolatos információt, a függvény utolsó végrehajtásának pillanatában; - TralingStop - a távolság a piaci ár és StopLoss kívánt értéke között (pontokban). A Tral_Stop() kereskedelmi függvény a Tral_Stop.mqh include fájlban megvalósítva: // // Tral_Stop.mqh // The code should be used for educational purpose only. // // Function modifying StopLosses of all orders of the given type // Global variables: // Mas_Ord_New Last known order array // int TralingStop Value of TralingStop (amount of points) // int Tral_Stop(int Tip) int Ticket; // Order ticket double Price, // Market order open price TS, // TralingStop (as related to the price) SL, // Value of order StopLoss TP; // Value of order TakeProfit bool Modify; // A criterion to modify. // for(int i=1;i<=mas_ord_new[0][0];i++) // Cycle for all orders // Searching for orders of the given type if (Mas_Ord_New[i][6]!=Tip) // If this is not our type.. continue; //.. skip the order Modify=false; // It is not assigned to be modified Price =Mas_Ord_New[i][1]; // Order open price SL =Mas_Ord_New[i][2]; // Value of order StopLoss TP =Mas_Ord_New[i][3]; // Value of order TakeProft Ticket=Mas_Ord_New[i][4]; // Order ticket

358 if (TralingStop<Level_new) // If it is less than allowed.. TralingStop=Level_new; //.. then the allowed one TS=TralingStop*Point; // The same in the relat, price value // switch(tip) // Go to Order type case 0 : // Order Buy if (NormalizeDouble(SL,Digits)<// If it is lower than we want.. NormalizeDouble(Bid-TS,Digits)) //..then modify it: SL=Bid-TS; // Its new StopLoss Modify=true; // Assigned to be modified. break; // Exit 'switch' case 1 : // Order Sell if (NormalizeDouble(SL,Digits)>// If it is higher than we want.. NormalizeDouble(Ask+TS,Digits) NormalizeDouble(SL,Digits)==0)//.. or zero(!) //..then modify it SL=Ask+TS; // Its new StopLoss Modify=true; // Assigned to be modified. // End of 'switch' if (Modify==false) // If there is no need to modify it.. continue; //..then continue the cycle bool Ans=OrderModify(Ticket,Price,SL,TP,0);//Modify it! // if (Ans==false) // Failed :( // Check for errors: if(errors(getlasterror())==false)// If the error is critical, return; //.. then leave. i--; // Decreasing counter Terminal(); // Order accounting function Events(); // Event tracking return; // Exit the user-defined function // Az 1-3 blokkban leírjuk a függvényben használt globális változókat, és létrehozzuk a lokális változókat. A 'for' ciklusban (3-6 blokk) kiválasztjuk az adott típusú megbízásokat és ha azok közül a megbízások közül bármelyiknek a StopLossa távolabb van az aktuális ártól, mint amit a felhasználó beállított, akkor azt a megbízást módosítjuk. Hogy áttekinthetővé tegyük a kódot, a megbízás néhány paraméterének értékét a Mas_Ord_New tömbből egyszerű változóknak adjuk (3-4 tömb). Majd a TralingStop változó szükséges ellenőrzését elvégezzük: Ha ennek a változónak az értéke kevesebb annál a megengedett minimum távolságnál, amit a dealing center beállított, akkor azt a minimális megengedett értékre fogjuk növelni. A 4-5 blokkban elvégezzük a megbízás típusa szerinti szükséges számításokat. Például, ha a Tip átvitt paraméter értéke 1 (egy Sell megbízást kell módosítani), a vezérlés a 'switch' operátor 'case 1' variációjába fog kerülni. Itt a StopLos módosításának szükségességét ellenőrizzük (a Követelmények és korlátozások a kereskedelemben részben ismertetett, a megbízástípusokra vonatkozó szabályok szerint). Ha a StopLos nincs beállítva vagy az a TralingStop értékénél távolabb van az aktuális piaci ártól, kiszámoljuk a StopLoss kívánt új értékét. A kereskedelmi kérést a következő sorban alakítjuk ki: bool Ans = OrderModify(Ticket,Price,SL,TP,0);//Modify it! Ismert, hogy e kereskedési stratégia alapján csak egy megbízás nyitása lehetséges. Azonban, a Tral_Stop() függvény lehetőséget nyújt arra, hogy több, azonos típusú megbízását módosítsunk. Ha a kereskedő nem avatkozik be kereskedelembe, az EA-nak nem szükséges több megbízást módosítania. Azonban, ha a kereskedő kézzel nyit egy megbízást (azokon felül, amelyik már nyitva van), el kell döntenünk, melyik a rendelkezésre álló megbízást módosítsuk elsőként. Amikor azt vizsgáltuk, hogy több meglévő megbízás közül melyiket kell először zárni, akkor eldöntöttük, hogy a bezárás sorrendjét a megbízás lot mérete alapján határozzuk meg. Ez a megoldás nyilvánvaló a megbízás méretét használja az EA a zárási sorrendet meghatározó ismertetőjelként. A módosítási sorrend problémájának nincs egyértelmű megoldása. Minden esetben a módosítás sorrendjének szempontját a

359 kereskedő stratégia lényege határozza meg. Ez az ismertetőjel lehet a lot összege, vagy az ha az egyik megbízásnak nincs beállított StopLoss értéke, illetve a StopLoss távolságok különbözőek. Sok esetben ezt az ismertetőjelet kifejezhetjük azzal az indexszel ami annak a veszteségnek a mérete, ami egy hirtelen árváltozásból eredhet, mikor minden tőzsdei megbízást automatikusan egyszerre bezár a StopLoss. A Tral_Stop() függvény fenti példájában, a megbízások módosítását véletlen sorrendbe valósítjuk meg - a piaci és függőben lévő megbízások megtalálásának sorrendjében. Minden speciális esetben a függvényt finomítani kell - a megbízások módosításának sorrendjét a speciális kereskedői stratégia szabályai szerint kell programozni. Fokozottan figyelni kell arra, hogy minden kereskedelmi művelet valós időjű módban történik. Ha túl sok megbízás van az EA-ban létrehozva akkor az nagy mennyiségű kereskedelmi kérést eredményez. Nyilvánvaló, hogy a piac megfordulhat, amíg azokat a kéréseket végrehajtják. Azonban a függvény nem küldi vissza a vezérlést a Trade() függvénybe, amíg minden szükséges módosítást végre nem hajt. Ez azt jelenti, hogy növekszik annak a veszélye, hogy kihagyjunk egy megbízás nyitó vagy záró kereskedelmi kérést. Ezért minden kereskedelmi stratégiát úgy kell kódolni, hogy egy időben ne legyen nagy mennyiségű megbízásunk. Az 5-6 blokkban a kereskedelmi kérések végrehajtása alatt történt hibákat elemezzük. Ha a hiba kritikus, a függvény be fogja fejezni a működését. Mindazonáltal, ha egy áthidalható hibát kapunk, az i számláló értékét 1-el csökkentjük. Ennek következtében ugyanazon megbízás módosítását ismét megkísérli a program a 'for' ciklus következő ismétlésénél. A legtöbb esetben a fenti kód végre fog hajtani minden szükséges módosítást. Ugyanakkor, ha bármilyen változás történik a meglévő megbízásokkal (például, egy megbízás a bezárul, mert a piaci ár elérte valamelyik stop szintet) a megbízások módosítása alatt, a Mas_Ord_New tömb szintén megváltozhat. Ez azt okozhatja, hogy valamelyik megbízás módosítása kimaradhat a start() függvény aktuális végrehajtása alatt. Ezt a következő ticknél, a start() függvény következő indításakor korrigálhatja a program

360 Hibafeldolgozó függvény Azokat a hibákat, amelyek a kereskedelmi kérések végrehajtása alatt előfordulnak, két csoportba sorolhatjuk: nem kritikus hibák és kritikus hibák. A nem kritikus hibák azok, amelyek a szervernél történnek. Miután őket kiküszöbölték, továbbra is tudunk kereskedni. Például, egy kérést visszautasíthat a bróker, ha az aktuális árról pillanatnyilag nincs információja. Ez a helyzet létrejöhet egy lassú piacon, mikor a tickek ritkán érkeznek. Vagy éppen ellenkezőleg, a bróker azért nem tudja végrehajtani a megbízásokat, mert egy élénk piacon túl sok kereskedelmi kérést kap. Ilyenkor a megbízást késve, vagy egyáltalán nem hajtják végre. Ilyen esetben az Expert Advisor folytathatja a működését, és egy kicsit később megismételheti a kérést a hibakód feldolgozása után. A kritikus hiba minden olyan hiba, ami lehetetlenné teszi a további kereskedést. Például, ha egy számlát zároltak, akkor nincs értelme kereskedelmi kéréseket küldeni. Ilyen esetben az EA-nak meg kell jelenítenie a megfelelő üzenetet és nem kell megismételnie a kérést. A hibafeldolgozó függvény használata nélkülözhetetlen egy szokásos EA-ban. Errors() hibafeldolgozó felhasználói függvény bool Errors( int Error ) A függvény visszaküldi a TRUE-t, ha a hiba nem kritikus. Különben visszaküldi a FALSE-t. Az Error paraméternek lehet bármilyen értéke és egyezhet bármilyen hibakóddal, ami a kereskedelem során előfordul. Az Errors() hibafeldolgozó felhasználói függvény az Errors.mqh inciude fájlban megvalósítva: // // Errors.mqh // The code should be used for educational purpose only. // // Error processing function. // Returned values: // true - if the error is overcomable (i.e. work can be continued) // false - if the error is critical (i.e. trading is impossible) // bool Errors(int Error) // Custom function // Error // Error number if(error==0) return(false); // No error Inform(15,Error); // Message // switch(error) // Overcomable errors: case 129: // Wrong price case 135: // Price changed RefreshRates(); // Renew data return(true); // Error is overcomable case 136: // No quotes. Waiting for the tick to come while(refreshrates()==false) // Before new tick Sleep(1); // Delay in the cycle return(true); // Error is overcomable case 146: // The trade subsystem is busy Sleep(500); // Simple solution RefreshRates(); // Renew data return(true); // Error is overcomable // Critical errors: case 2 : // Common error case 5 : // Old version of the client terminal case 64: // Account blocked case 133: // Trading is prohibited default: // Other variants return(false); // Critical error //

361 // Az Errors() hibafeldolgozó függvény algoritmusának létrehozása során fölmerül a kérdés: A függvénynek milyen értéket kell visszaküldenie, ha az adott paraméter értéke 0 (nincs hiba). Egy helyesen kódolt EA-ban ez a helyzet nem fordul elő. Azonban, a kód módosítása során néha lehet a hiba értéke 0 (ha olyankor hívjuk a függvényt, amikor valójában nincs hiba). Tehát ésszerű hozzáadni néhány sort a függvényhez a kód kezdetén (a 2-3 blokkban), arra az esetre, ha az Error változó nulla lenne. Az Errors() függvény válasza az Error változó nulla értékére a hibafeldolgozó algoritmustól függ. Az Errors() függvény visszatérési értékét figyelembe veszi az EA a kereskedelmet végrehajtható függvényében. Ha az Errors() függvény visszatérési értéke 'true' (a hiba nem kritikus), akkor a program újra próbálkozik kereskedelem végrehajtásával. Ha ez az érték 'false' akkor a kereskedelmi függvény végrehajtása megszakad, és a vezérlés visszakerül a hívó függvénybe, azután a start() függvénybe, azután az ügyfélterminálra. Ha a visszaküldött érték kezelésének ez a két alternatívája van, akkor abban az esetben, amikor nincs hiba (Error=0), a második alternatívát kell választani, és 'false értéket kell visszaküldeni. Ez a garancia arra, hogy az egy egyszer végrehajtott kérés nem lesz megismételve. Amint a hibáról az üzenetet megjeleníti az Inform() függvény, a vezérlés a 3-4 blokkba a 'switch' operátorhoz kerül. A különböző hibakódok más case variáció végrehajtását eredményezik. Például, ha 136 hiba történik, ez azt jelenti, hogy a brókernek nincs információja az aktuális árról, ezért nem tud megfelelő döntést hozni. Ez a helyzet nem fog megváltozni, amíg nem jön egy új tick, úgyhogy fölösleges megismételni ugyanazt a kereskedelmi kérést, mert azt úgysem fogják végrehajtani. A helyes megoldás ebben az esetben a szünet tartása - az EA minden kezdeményezésének felfüggesztése. Egyszerű módszer egy új tick észlelésére a RefreshRates() függvény visszatérési értékének vizsgálata. A vezérlést vissza fogjuk küldeni a hívott függvénybe, amiben a kereskedelmi kérést megismételjük, (a megfelelő elemzés után, ha ez szükséges), amint az új tick érkezik. Ha egy olyan hiba történik, amiről a programozó úgy véli, hogy kritikus hiba, a függvény visszatérési értéke 'false'. Ebben az esetben a kérést nem fogjuk megismételni, tehát szükségtelen bármit is tenni az Errors() függvényben. Minden hibát, amit nem dolgoztunk fel kritikus hibának tekintünk. Kiterjeszthetjük a feldolgozott hibák listáját (lásd: Hibakódok)

362 Az összetett programok általános jellemzői Nincs egyetlen formális jellemző, ami megkülönböztetné a szokásos és összetett programokat. Általánosságban elmondható, hogy a komplex programok pozitív módon különböznek az eszközök mennyisége és az információ feldolgozása alapján. A komplex programokat szinte csak az előnyös tulajdonságaikkal lehet jellemezni. A programvégrehajtás rendje A fő szabály az, hogy egy szokásos program kódját a különleges start() függvény tartalmazza, és azt az ügyfélterminál indítja el végrehajtásra. A legtöbb esetben a start() végrehajtási ideje lényegesen rövidebb mit a tickek közti távolság. Ez azt jelenti, hogy az idő legnagyobb részében a program vár egy tick érkezésére. Ezt a folyamatot a működés-szünet arányával jellemezhetjük. A start() Т1 végrehajtási ideje a milliszekundum tartományban van, és a tickek közti Т2 idő átlag 5 másodperc. Így, az EA szünet/működés aránya elérheti a Т2/Т1=1000-et vagy többet (lásd 159. ábrát). Ezért azt kell mondani, hogy egy szokásos EA hatásos működési kapacitása az egész idő 0,1 százaléka, az idő többi részében nem működik. Némelyik EA összetett számításokat hajthat végre és a start() végrehajtási időtartama hosszabb lehet, és tíz másodpercekig is eltarthat. Ezekben az esetekben a start() függvény nem fog elindulni minden ticknél, hanem csak azokon a tickeken, amik akkor jöttek, amikor a start() vár rájuk. A 159. ábrán azt az esetet láthatjuk, amikor a tick a start() függvény végrehajtási időszaka alatt érkezik (a t4 pillanatnál) és ez nem indítja el új különleges start() függvény működését. Legközelebb a start() függvény a t5 pillanatnál fog kezdődni. Az aktuális start() függvény végrehajtás vége és a következő végrehajtásának a kezdete között e miatt szünet keletkezik 159. ábra. A start() függvény szakaszos és ciklikus végrehajtása közötti különbség. Van egy módszer, amellyel lényegesen növelni lehet a program hatékonyságát, miközben csökkentjük a működés/szünet arányát. Ezt egy olyan algoritmussal valósítjuk meg, amely szerint a fő kódot (végtelenül) sok alkalom hajtjuk végre a start() végrehajtás alatt (ez csak EA-ban és scriptekben megengedett). A példát az összehurkolt start() függvényről lent láthatjuk: // start() // Special function start() while(!isstopped()) // Until user.. //..stops execution of the program RefreshRates(); // Data renewal //...The main code of the program is specified here Sleep(5); // Short pause return; // Control is returned to the terminal // Az egész kódot a "while ciklusoperátor testben helyezzük el, és az egyetlen mód, hogy kilépjünk a ciklusból, az ügyféltermináltól kapott parancs, hogy fejezze be a programvégrehajtást. Ha a start() függvényt (amire a bemutatott megoldást fölépítettük) egyszer elindítjuk, az végtelen sokáig fog működni,

363 és az ügyfélterminálra a vezérlést csak akkor fogja visszaküldeni, amikor a felhasználó kézzel leválasztja az EA-t az ablakról, vagy néhány másik feltétel teljesül (lásd: Special Functions). A start() függvényben a ciklus végrehajtása folyamatosan fut, ezért nincs olyan időszak, amikor a program tick váró üzemmódban van (lásd: 159. ábrát), ezzel az eljárással a működés/szünet aránya egyenlő 1-gyel. Azt a start() függvényt, ami az ismertetett elven alapul, csak egyszer indítjuk el az ügyfélterminálban. Ezért az információ frissítéséhez (a piaci folyamatok követéséhez) minden ciklusban kötelező használni a RefreshRates() függvényt. Azért, hogy elkerüljük a rendszer túlterhelését, minden ciklus végén rövid szünetet kell beiktatni. A ciklikus programok fejlesztése során sok figyelmet igényel az algoritmus megkomponálása. Például, egy kritikus hiba esetén a szokásos program megszakítja a start() függvény végrehajtását és a vezérlés visszatér az ügyfélterminálhoz. Egy ciklikus program a vezérlést folyamatosan magánál tartja és minden lehetséges eseményre reagálnia kell, így a kereskedelem tiltására is. De a kereskedés ideiglenes tiltása nem gátolja a program végrehajtását. A működésének egész időszak alatt a programnak az elérhető információ egészét fel kell dolgoznia, a felhasználó beleavatkozásait is beleértve. Általában, egy ilyen program sokkal hatékonyabb egy szokásos programmal összehasonlítva. Az elérhető eszközök A ciklikus programok használatának csak akkor van értelme, ha a programvégrehajtás folytonosságát valóban kihasználjuk. Például egy ilyen program kontrolálhatja a kereskedő utasításait. A grafikus objektumok koordinátáinak a módosítását, vagy másik programok (scriptek és indikátorok) csatolását használhatjuk a beavatkozásra. Egy egyszerű program néhány eseményre reagálhat (beleértve a felhasználó beavatkozását) és a különleges start() függvény végrehajtása főszabályként a legközelebbi ticknél indul. Azonban egy ciklikus program azonnal fel tud dolgozni minden eseményt (!). Ebben az esetben az események feldolgozásának késleltetése rövidebb, mint a start() függvény egy ciklusának a végrehajtási ideje (majdnem mindig kevesebb, mint ms). Egy összetett programban arra használhatjuk a grafikus objektumokat, hogy bemutassuk a megbízások jellemző értékeit. Például minden típusú megbízást zöld vonallal jelölünk a charton, míg a stop értékeket piros vonalakkal. Ha egyidejűleg több megbízás van a képernyőn eléggé nehéz megkülönböztetni, hogy valamely vonal melyik megbízáshoz tartozik. De ha ezeknek a grafikus objektumoknak minden megbízás esetében különböző színt és/vagy vonalstílust adunk sokkal könnyebben eligazodunk a megbízások és stopszintjeik között. Továbbá ezen objektumok koordinátáinak (kézzel történő) megváltoztatását a megbízás módosítására irányuló utasításként használhatjuk a programban. Például ha a felhasználó elmozdít fölfelé egy olyan vízszintes vonalat, ami egy függőben levő megbízást jelképez, ennek következtében a program küldhet a szerverre egy olyan kereskedelmi kérést, amelyben módosítja a megbízást (az előre beállított nyitó árat növeli az új értékre) (az azonnali végrehajtás miatt az összehurkolt program használata elengedhetetlen). Így egy összetett programmal lehetővé tehetjük a kereskedés egérrel történő végrehajtását. Az összetett programokban olyan függvényeket is használhatunk, melyekkel külön-külön módosíthatjuk a stop megbízásokat vagy a függő megbízások nyitó árát. Ezekkel a függvényekkel létrehozhatunk a megbízást jelölő vonal közelében egy grafikus objektumot, például egy nyilat, amellyel jelezzük, hogy a függvény végrehajtása folyamatban van. Grafikus objektumok segítségével megtervezheted a kereskedés menetét. Például, ha az aktuális ártól egy bizonyos távolságra elhelyezel egy Pivot jelet ezzel tájékoztatod a programot, hogy be kell zárni a megbízást és nyitni kell egy ellentétest, amikor a piaci ár a megadott szintet eléri. Hasonló módon módosíthatod a programban a limit szintet, a záró árat stb Olyan grafikus objektumok használata, amelyek megjelenítik egy program beállításait, növelik a kereskedő tájékozottságát a jelenlegi és a megtervezett eseményekről. Az eseményekkel kapcsolatos hangjelzéseket az összetett programokban szintén használhatunk. A hangjelzések használata a programban lehetővé teszik a kereskedőnek, hogy otthagyja a PC-t és a különböző hangjelzések alapján tájékozódjon az eseményekről (dallamok, lejátszott szöveg.. stb.). A kereskedés hatékonysága nagyban függ a fontos gazdasági és politikai bejelentések időpontjának ismeretétől. A legtöbb bróker ellátja a kereskedőket a következő héten várható bejelentések időpontjának és a hírek jelentőségének listájával. A közelgő eseményekkel kapcsolatos információt egy fájlba írhatjuk. A

364 működése során a program kiolvashatja az információt a fájlból és a közelgő esemény fontosságától függően különböző kereskedelmi forgatókönyveket hajthat végre. Például a program a függőben levő megbízásokat törölheti a fontos bejelentések előtt, a program befejezheti a kereskedést, módosíthatja a megbízások stop szintjeit stb. - bezárhat minden nyitott megbízást miközben előzetesen tájékoztatja a kereskedőt. A műveletek automatizált és kézi végrehajtása Egy összetett programot az események feldolgozásának összetettebb algoritmusa jellemez. Különösen ez a fajta program feltételezi, hogy a kereskedő valamilyen módon beavatkozik a kereskedés folyamatába. Például ha a kereskedési stratégia csak egy megbízást enged meg és a kereskedő nyit még egy megbízást, egy összetett program miután észleli ezt a fajta eseményt, elindítja egy olyan algoritmus végrehajtását, amely kezeli ezt a helyzetet. A program először a jogosulatlan beavatkozásról figyelmeztetheti a kereskedőt, és javasolhatja, hogy törölje ezt a rendellenes megbízást. Ha ez nem történik meg, a program (a beállításoktól függően) törölni tudja a rendellenes megbízást, vagy ki tud lépni az automatizált kereskedési módból, amiről előbb tájékoztatja a kereskedőt. Ha egy program végrehajtását olyan esetben indítjuk, amikor már több nyitott vagy függő megbízásunk van, a beállításoktól függően különböző műveleteket hajthat végre. Például a program a kereskedő jóváhagyása nélkül bezárhat minden ellentétes megbízást. Ha a kereskedési stratégia nem engedi meg a függőben levő megbízásokat, meghatározott szempontok alapján törölheti ezeket először a piaci árhoz a legközelebbit, aztán a legnagyobbat, stb. Ha a kereskedő a stratégiája alapján beállítja a kötésméretet és a megbízások beállítandó stop szintjeit, a program (a piaci események folytonos felügyelete mellett) javaslatot tehet a kereskedőnek az automatizált kereskedő mód aktiválására, és ha a kereskedő ezzel egyetért, grafikus objektumok segítségével kiválaszthatja a végrehajtandó kereskedési forgatókönyvet. Minden kereskedőnek megvan a saját kereskedési preferenciája. Néhány kereskedő csak automatizált kereskedést fojtat, míg mások a fél-automatizáltat, megint mások jobban szeretik a kézi módot. Egy helyesen tervezett programnak ki kell elégítenie minden követelményt, minden felhasználási módnak beállíthatónak kell lennie. Például a program kézi üzemmódban tanácsokat adhat szöveges információt nyújthat, valamint grafikus objektumok segítségével jelezheti a trend irányt, mutathatja a támasz szinteket stb. A program kérhet engedélyt a kereskedőtől a megbízás nyitása előtt, javaslatot tehet a kereskedőnek a kézi beavatkozásra (például a kézi stop módosításra) ha félautomata módban működik. Abban az esetben, ha a program mindenféle beavatkozás nélkül automatizált módban működik, bizonyos események bekövetkezésekor átválthat félautomata vagy kézi üzemmódra. Egy program minden fent említett funkcióját megvalósíthatjuk a speciális MQL4 programnyelv segítségével, amit direkt erre a célra terveztek. Egy helyesen tervezett, összetett program kétségbe vonhatatlan előnye, hogy a kereskedő gyorsan hozzászokik a használatához a kereskedés során

365 Függelék (angolul) [Appendixes] Glossary. Notions and terms used. Types of Trades. Types of orders that can be used in MetaTrader 4 Client Terminal. Requirements and Limitations in Making Trades. Some limitations for opening, closing, modifying and deleting orders. Error Codes. There is a possibility of an error appearing while program is running. It is necessary to define the handling of the most important errors in advance. You can get the error code by using the GetLastError() function. Styles of Indicator Lines Displaying. Custom indicators allow you to display the graphical information using different styles. The DRAW_LINE style can draw lines of a predefined width and style. For DRAW_ARROW style it is necessary to specify the displayed arrow code in the Windings format. The DRAW_SECTION style uses one indicator buffer, while the DRAW_ZIGZAG style requires two indicator buffers: even and odd. Knowing styles of drawing lets you combine different methods of information showing in one indicator. Types and Properties of Graphical Objects. There are 24 built-in graphical objects that can be created via a program. This feature allows to provide indicators and EAs with additional abundant visualization tools. Common object properties like, for example, junction point and object color can be set, as well as properties for an individual graphical object. When some object properties have been changed, the object can be immediately (forcedly) redrawn using the WindowsRedraw() function. Wave Files. You can redefine the set of the wave files that are used in MetaTrader 4. For doing this open the "Tools"-"Options" window and specify necessary wave files on the "Events" tab. You can play the attached files in your own programs using the PlaySound() function. Function MessageBox() Return Codes. The MessageBox() function allows you to organize the interaction between a program and a user during the execution process directly. Processing return codes of the MessageBox() window allows to guide the program running depending on the button pressed by a user. It makes the program more flexible. Query Identifiers Used in the MarketInfo() Function. The MarketInfo() function allows you to get different information about a trading account, security properties and trade server settings. List of Programs. All programs that were used in this book

366 Glossary Algorithm is a precise instruction for completing a preset act sequence; control in an executable program is transferred according to the algorithm. Application Program is a program coded in MQL4 and used in MetaTrader 4 Client Terminal; it can be an Expert Advisor, a Script or an Indicator. Array - is an arranged set of the values of one-type variables that have a common name. Arrays can be one-dimensional and multidimensional. The maximum admissible amount of dimensions in an array is four. Arrays of any data types are allowed. Array Component is a part of an array; it is an indexed variable having the same name and a value. Ask is the higher of two prices offered by broker in a Two-Way Quote for a Security. Bar is a graphical representation of a price chart. Bar is characterized by Open, Close, High, Low prices, as well as by Volume and Time (see also Candlestick). Bid is the lower of two prices offered by broker in a Two-Way Quote for a Security. Buffer is a memory area containing numeric values of an indicator array. Built-In Function is the same as a standard function. Buy is a market order that defines buying of assets for a security. BuyLimit is a pending order to buy assets for a security at a price lower than the current one. The order will be executed (modified into market order Buy) if the Ask price reaches or falls below the price set in the pending order. BuyStop is a pending order to buy assets for a security at a price higher than the current one. The order will be executed (modified into market order Buy) if the Ask price reaches or rises above the price set in the pending order. Candlestick is a graphical representation of a price chart. Candlestick is characterized by Open, Close, High, Low prices, as well as by Volume and Time. The can be black candlesticks and white candlesticks (see also Bar)

367 Comment is an optional and nonexecutable part of a program. Constant is a program constituent; it is an object that has a value. Constant Expression is an expression consisting of constants and operations, for example: 2+3*7. A constant expression is calculated at compile time. Control is execution of actions predefined by the program algorithm and by the properties of the client terminal. Control may be transferred within a program from one program line to another, as well as between the program and the client terminal (see Some Basic Concepts). Currency Instrument is a currency pair, for example, EURUSD, GBPCHF, etc.; it is a particular security (symbol). Custom Indicator is an application program coded in MQL4; it is basically intended for graphical displaying of preliminarily calculated dependences. The values of components ofindicator Arrays of a custom indicator are available to other applications through the function named icustom() (see also Technical Indicator). EA is an abbreviation for Expert Advisor (see also Script and Indicator). Expert Advisor is a program coded in MQL4; it is distinguished by the properties of special function start() called by the client terminal to be executed on every tick; the main purpose of Expert Advisors is programmed control over trades (see also EA, Script and Indicator). Expression is a sequence of operands and operations; it is a program record, the calculated value of which is characterized by a data type. External Variable is a variable, the value of which is available from the program properties window; it has the properties of a global variable. File Descriptor is the unique number of a file opened by an executable program at the current moment. File Pointer a location in a file where the reading of the next value starts. As the data are read, the pointer shifts to the right by one or several positions. File Separator is a special character; it is a record to be stored in the file to separate data records. Flag is a variable the value of which is placed in correspondence with some events or facts. Formal Parameters represent a list of variables given in the header of Function Description (see also Functions and Function Description and Operator 'return')

368 Function is a named, specific part of a program that describes the method of data conversion. The use of a function in a program involves Function Description and Function Call. There can be special, standard (built-in) and custom functions (see also Functions and Special Functions). Function Body is one or several operators being the executable part of Function Description. Function Call (or Function Reference) is a record, execution of which results in execution of a function (see also Function Description). Function Description is a specific named part of a program intended for execution; it consists of two basic parts - a function header and a function body; it is used in special andcustom functions (see also Functions, Function Description and Operator return and Special Functions). Function Header is a part of function description; it consists of the type of the return value, function name and the list of formal parameters. The list of formal parameters is enclosed in parentheses and placed after the function name. Function Reference is the same as Function Call. Global Variable is a variable declared beyond all functions. The Scope of global variables is the entire program. Global Variable of Client Terminal is a variable, the value of which is available from all applications launched in the client terminal (abbreviated form: GV). Graphical Object - is a shape in the symbol window; the shape can be selected, moved, modified or deleted. GV is an abbreviation for Global Variable of the Client Terminal. Indicator is a built-in function of the client terminal or a program coded in MQL4; the main purpose of indicators is to display Indicator Lines on the screen; indicators cannot make trades; there are two types of indicators: Custom Indicators and Technical Indicators (see also Expert Advisor, EA and Script). Indicator Array is a one-dimensional array containing numeric values that are the basis for constructing of Indicator Line. Indicator Line is a graphical display of a certain dependence based on numeric values included in an Indicator Array. Initialization of Variable is assigning a type-dependent value to the variable at Variable Declaration. Iteration is a repeated execution of some calculations; it is used to note that the program lines composing the cycle operator body (see also Cycle Operator "while" and Cycle Operator "for") are executed. Local Variable is a variable declared within a function. The Scope of local variables is the body of the function, in which the variable is declared. Local Time is the time set on a local PC (see also Server Time). Loop (Cycle) Body is one or several operators included in braces; it is located directly after the cycle operator header (see Cycle Operator "while" и Cycle Operator "for"). Looping is a continuously repeated execution of operators composing the loop (cycle) body; it's a critical situation that results from realization of a wrong algorithm

369 Market Order is an executed order to buy or sell assets for a symbol (security). A market order is displayed in the symbol window and in the 'Terminal' window until the order is closed (see also Pending Order). Normalized Price is a price rounded off to one Point size for a security (symbol). Operand is a Constant, a Variable, an array component or a value returned by a function (see Function Call). Operation is an action made upon Operands (see also Operation Symbol). Operation Symbol is a preset character or a group of characters that order to execute an operation. Operator is a part of a program; it is a phrase in an algorithmic language that prescribes a certain method of data conversion. There can be simple and compound operators. Operator Format is a set of rules for formatting of an operator of the given type. Each operator type has its own format (see also Operators). Opposite (Counter) Order is a market order opened in the direction opposite to the direction of another market order opened for the same symbol. Pending Order is a trade order to buy or sell assets for a security (a symbol) when the preset price level is reached. The pending order is displayed in the symbol window and in the 'Terminal' window until it becomes a market order or is deleted (see also Market Order). Point is the unit of price measurement for a security (the minimum possible price change, the last significant figure of the price value). Predefined Variable is a variable with a predefined name; the value of such variable is defined by the client terminal and cannot be changed by coding (see also Predefined Variables). Regular Loop Exit is transfer of control outside the cycle operator as a result of execution of a condition placed in the cycle operator header (see also Special Loop Exit). Script is a program coded in MQL4; it is marked by properties of the special function of start() called by the client terminal to be executed only once; scripts are intended for performing any operations that should be implicitly executed only once (see also Expert Advisor, EA and Indicator). Security (Symbol) is the name of the object to be quoted. Sell is a market order that defines selling of assets for a security. SellLimit is a pending order to sell assets for a security at a price higher than the current one. The order will be executed (modified into market order Sell) if the Bid price reaches or rises above the price set in the pending order

2. Fejezet : Számrendszerek

2. Fejezet : Számrendszerek 2. Fejezet : Számrendszerek The Architecture of Computer Hardware and Systems Software: An Information Technology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003 Wilson Wong, Bentley College

Részletesebben

SZÁMÉRTÉKEK (ÁT)KÓDOLÁSA

SZÁMÉRTÉKEK (ÁT)KÓDOLÁSA 1 ELSŐ GYAKORLAT SZÁMÉRTÉKEK (ÁT)KÓDOLÁSA A feladat elvégzése során a következőket fogjuk gyakorolni: Számrendszerek közti átváltás előjelesen és előjel nélkül. Bináris, decimális, hexadexcimális számrendszer.

Részletesebben

Járműfedélzeti rendszerek II. 1. előadás Dr. Bécsi Tamás

Járműfedélzeti rendszerek II. 1. előadás Dr. Bécsi Tamás Járműfedélzeti rendszerek II. 1. előadás Dr. Bécsi Tamás A tárgy órái Előadás hetente (St101) csüt. 8:15 Bécsi Tamás C elmélet Ajánlott irodalom Dennis Ritchie: A C programozási nyelv Gyakorlat hetente

Részletesebben

Programozás alapjai gyakorlat. 2. gyakorlat C alapok

Programozás alapjai gyakorlat. 2. gyakorlat C alapok Programozás alapjai gyakorlat 2. gyakorlat C alapok 2016-2017 Bordé Sándor 2 Forráskód, fordító, futtatható állomány Először megírjuk a programunk kódját (forráskód) Egyszerű szövegszerkesztőben vagy fejlesztőkörnyezettel

Részletesebben

Java II. I A Java programozási nyelv alapelemei

Java II. I A Java programozási nyelv alapelemei Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2008. 02. 19. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve az annak

Részletesebben

Kifejezések. Kozsik Tamás. December 11, 2016

Kifejezések. Kozsik Tamás. December 11, 2016 Kifejezések Kozsik Tamás December 11, 2016 Kifejezések Lexika Szintaktika Szemantika Lexika azonosítók (változó-, metódus-, típus- és csomagnevek) literálok operátorok, pl. + zárójelek: (), [], {},

Részletesebben

Készítette: Nagy Tibor István

Készítette: Nagy Tibor István Készítette: Nagy Tibor István Operátorok Műveletek Egy (vagy több) műveleti jellel írhatók le A műveletet operandusaikkal végzik Operátorok fajtái operandusok száma szerint: egyoperandusú operátorok (pl.:

Részletesebben

The Architecture of Computer Hardware and Systems Software: An InformationTechnology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003

The Architecture of Computer Hardware and Systems Software: An InformationTechnology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003 . Fejezet : Számrendszerek The Architecture of Computer Hardware and Systems Software: An InformationTechnology Approach. kiadás, Irv Englander John Wiley and Sons Wilson Wong, Bentley College Linda Senne,

Részletesebben

Segédlet az Informatika alapjai I. című tárgy számrendszerek fejezetéhez

Segédlet az Informatika alapjai I. című tárgy számrendszerek fejezetéhez Segédlet az Informatika alapjai I. című tárgy számrendszerek fejezetéhez Sándor Tamás, sandor.tamas@kvk.bmf.hu Takács Gergely, takacs.gergo@kvk.bmf.hu Lektorálta: dr. Schuster György PhD, hal@k2.jozsef.kando.hu

Részletesebben

A C# programozási nyelv alapjai

A C# programozási nyelv alapjai A C# programozási nyelv alapjai Tisztán objektum-orientált Kis- és nagybetűket megkülönbözteti Ötvözi a C++, Delphi, Java programozási nyelvek pozitívumait.net futtatókörnyezet Visual Studio fejlesztőkörnyezet

Részletesebben

4. Fejezet : Az egész számok (integer) ábrázolása

4. Fejezet : Az egész számok (integer) ábrázolása 4. Fejezet : Az egész számok (integer) ábrázolása The Architecture of Computer Hardware and Systems Software: An Information Technology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003 Wilson

Részletesebben

Tömbök kezelése. Példa: Vonalkód ellenőrzőjegyének kiszámítása

Tömbök kezelése. Példa: Vonalkód ellenőrzőjegyének kiszámítása Tömbök kezelése Példa: Vonalkód ellenőrzőjegyének kiszámítása A számokkal jellemzett adatok, pl. személyi szám, adószám, taj-szám, vonalkód, bankszámlaszám esetében az elírásból származó hibát ún. ellenőrző

Részletesebben

3 A C programozási nyelv szintaktikai egységei

3 A C programozási nyelv szintaktikai egységei 3 A C programozási nyelv szintaktikai egységei 3.1 Azonosítók Betűk és számjegyek sorozata, betűvel vagy _ (aláhúzás) karakterrel kell kezdődnie. A nagy- és kisbetűk különbözőek. Az azonosítók tetszőleges

Részletesebben

Webprogramozás szakkör

Webprogramozás szakkör Webprogramozás szakkör Előadás 5 (2012.04.09) Programozás alapok Eddig amit láttunk: Programozás lépései o Feladat leírása (specifikáció) o Algoritmizálás, tervezés (folyamatábra, pszeudokód) o Programozás

Részletesebben

Készítette: Nagy Tibor István

Készítette: Nagy Tibor István Készítette: Nagy Tibor István A változó Egy memóriában elhelyezkedő rekesz Egy értéket tárol Van azonosítója (vagyis neve) Van típusa (milyen értéket tárolhat) Az értéke értékadással módosítható Az értéke

Részletesebben

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar Programozás I. 3. gyakorlat Szegedi Tudományegyetem Természettudományi és Informatikai Kar Antal Gábor 1 Primitív típusok Típus neve Érték Alap érték Foglalt tár Intervallum byte Előjeles egész 0 8 bit

Részletesebben

SZÁMRENDSZEREK KÉSZÍTETTE: JURÁNYINÉ BESENYEI GABRIELLA

SZÁMRENDSZEREK KÉSZÍTETTE: JURÁNYINÉ BESENYEI GABRIELLA SZÁMRENDSZEREK KÉSZÍTETTE: JURÁNYINÉ BESENYEI GABRIELLA BINÁRIS (kettes) ÉS HEXADECIMÁLIS (tizenhatos) SZÁMRENDSZEREK (HELYIÉRTÉK, ÁTVÁLTÁSOK, MŰVELETEK) A KETTES SZÁMRENDSZER A computerek világában a

Részletesebben

3. Ezután a jobb oldali képernyő részen megjelenik az adatbázistábla, melynek először a rövid nevét adjuk meg, pl.: demo_tabla

3. Ezután a jobb oldali képernyő részen megjelenik az adatbázistábla, melynek először a rövid nevét adjuk meg, pl.: demo_tabla 1. Az adatbázistábla létrehozása a, Ha még nem hoztunk létre egy adatbázistáblát sem, akkor a jobb egérrel a DDIC-objekt. könyvtárra kattintva, majd a Létrehozás és az Adatbázistábla menüpontokat választva

Részletesebben

C programozási nyelv

C programozási nyelv C programozási nyelv Előfeldolgozó utasítások Dr Schuster György 2011 május 3 Dr Schuster György () C programozási nyelv Előfeldolgozó utasítások 2011 május 3 1 / 15 A fordítás menete Dr Schuster György

Részletesebben

Occam 1. Készítette: Szabó Éva

Occam 1. Készítette: Szabó Éva Occam 1. Készítette: Szabó Éva Párhuzamos programozás Egyes folyamatok (processzek) párhuzamosan futnak. Több processzor -> tényleges párhuzamosság Egy processzor -> Időosztásos szimuláció Folyamatok közötti

Részletesebben

Szkriptnyelvek. 1. UNIX shell

Szkriptnyelvek. 1. UNIX shell Szkriptnyelvek 1. UNIX shell Szkriptek futtatása Parancsértelmez ő shell script neve paraméterek shell script neve paraméterek Ebben az esetben a szkript tartalmazza a parancsértelmezőt: #!/bin/bash Szkriptek

Részletesebben

Assembly programozás: 2. gyakorlat

Assembly programozás: 2. gyakorlat Assembly programozás: 2. gyakorlat Számrendszerek: Kettes (bináris) számrendszer: {0, 1} Nyolcas (oktális) számrendszer: {0,..., 7} Tízes (decimális) számrendszer: {0, 1, 2,..., 9} 16-os (hexadecimális

Részletesebben

Harmadik gyakorlat. Számrendszerek

Harmadik gyakorlat. Számrendszerek Harmadik gyakorlat Számrendszerek Ismétlés Tízes (decimális) számrendszer: 2 372 =3 2 +7 +2 alakiérték valódi érték = aé hé helyiérték helyiértékek a tízes szám hatványai, a számjegyek így,,2,,8,9 Kettes

Részletesebben

ÁTVÁLTÁSOK SZÁMRENDSZEREK KÖZÖTT, SZÁMÁBRÁZOLÁS, BOOLE-ALGEBRA

ÁTVÁLTÁSOK SZÁMRENDSZEREK KÖZÖTT, SZÁMÁBRÁZOLÁS, BOOLE-ALGEBRA 1. Tízes (decimális) számrendszerből: a. Kettes (bináris) számrendszerbe: Vegyük a 2634 10 -es számot, és váltsuk át bináris (kettes) számrendszerbe! A legegyszerűbb módszer: írjuk fel a számot, és húzzunk

Részletesebben

Mit tudunk már? Programozás alapjai C nyelv 4. gyakorlat. Legnagyobb elem keresése. Feltételes operátor (?:) Legnagyobb elem keresése (3)

Mit tudunk már? Programozás alapjai C nyelv 4. gyakorlat. Legnagyobb elem keresése. Feltételes operátor (?:) Legnagyobb elem keresése (3) Programozás alapjai C nyelv 4. gyakorlat Szeberényi Imre BME IIT Mit tudunk már? Típus fogalma char, int, float, double változók deklarációja operátorok (aritmetikai, relációs, logikai,

Részletesebben

Programozás BMEKOKAA146. Dr. Bécsi Tamás 2. előadás

Programozás BMEKOKAA146. Dr. Bécsi Tamás 2. előadás Programozás BMEKOKAA146 Dr. Bécsi Tamás 2. előadás Szintaktikai alapok Alapvető típusok, ismétlés C# típus.net típus Méret (byte) Leírás byte System.Byte 1Előjel nélküli 8 bites egész szám (0..255) char

Részletesebben

AWK programozás, minták, vezérlési szerkezetek

AWK programozás, minták, vezérlési szerkezetek 10 AWK programozás, minták, vezérlési szerkezetek AWK adatvezérelt szkriptnyelv text processing, adat kiterjesztés, tagolt adatok automatizált soronkénti feldolgozása a forrásállományt soronként beolvassa

Részletesebben

Programozás alapjai C nyelv 4. gyakorlat. Mit tudunk már? Feltételes operátor (?:) Típus fogalma char, int, float, double

Programozás alapjai C nyelv 4. gyakorlat. Mit tudunk már? Feltételes operátor (?:) Típus fogalma char, int, float, double Programozás alapjai C nyelv 4. gyakorlat Szeberényi Imre BME IIT Programozás alapjai I. (C nyelv, gyakorlat) BME-IIT Sz.I. 2005.10.10.. -1- Mit tudunk már? Típus fogalma char, int, float,

Részletesebben

Kedves Diákok! A feladatok legtöbbször egy pontot érnek. Ahol ettől eltérés van, azt külön jelöljük.

Kedves Diákok! A feladatok legtöbbször egy pontot érnek. Ahol ettől eltérés van, azt külön jelöljük. Kedves Diákok! Szeretettel köszöntünk Benneteket abból az alkalomból, hogy a Ceglédi Közgazdasági és Informatikai Szakközépiskola informatika tehetséggondozásának első levelét olvassátok! A tehetséggondozással

Részletesebben

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek Programozás alapjai gyakorlat 4. gyakorlat Konstansok, tömbök, stringek Házi ellenőrzés (f0069) Valósítsd meg a linuxos seq parancs egy egyszerűbb változatát, ami beolvas két egész számot, majd a kettő

Részletesebben

1. Alapok. #!/bin/bash

1. Alapok. #!/bin/bash 1. oldal 1.1. A programfájlok szerkezete 1. Alapok A bash programok tulajnképpen egyszerű szöveges fájlok, amelyeket bármely szövegszerkesztő programmal megírhatunk. Alapvetően ugyanazokat a at használhatjuk

Részletesebben

HUNGÁRIA ONLINE TRADER fx 4

HUNGÁRIA ONLINE TRADER fx 4 HUNGÁRIA ONLINE TRADER fx 4 Felhasználói kézikönyv Tartalom: A HOTfx4 indítása A HOTfx4 felhasználói felülete Hogyan helyezzünk el megbízást Hogyan módosítsunk megbízást Hogyan helyezzünk el függő megbízást

Részletesebben

Adatszerkezetek Tömb, sor, verem. Dr. Iványi Péter

Adatszerkezetek Tömb, sor, verem. Dr. Iványi Péter Adatszerkezetek Tömb, sor, verem Dr. Iványi Péter 1 Adat Adat minden, amit a számítógépünkben tárolunk és a külvilágból jön Az adatnak két fontos tulajdonsága van: Értéke Típusa 2 Adat típusa Az adatot

Részletesebben

C programozási nyelv Pointerek, tömbök, pointer aritmetika

C programozási nyelv Pointerek, tömbök, pointer aritmetika C programozási nyelv Pointerek, tömbök, pointer aritmetika Dr. Schuster György 2011. június 16. C programozási nyelv Pointerek, tömbök, pointer aritmetika 2011. június 16. 1 / 15 Pointerek (mutatók) Pointerek

Részletesebben

sallang avagy Fordítótervezés dióhéjban Sallai Gyula

sallang avagy Fordítótervezés dióhéjban Sallai Gyula sallang avagy Fordítótervezés dióhéjban Sallai Gyula Az előadás egy kis példaprogramon keresztül mutatja be fordítók belső lelki világát De mit is jelent, az hogy fordítóprogram? Mit csinál egy fordító?

Részletesebben

Baran Ágnes. Gyakorlat Függvények, Matlab alapok

Baran Ágnes. Gyakorlat Függvények, Matlab alapok Matematika Mérnököknek 1. Baran Ágnes Gyakorlat Függvények, Matlab alapok Matematika Mérnököknek 1. A gyakorlatok fóliái: https://arato.inf.unideb.hu/baran.agnes/oktatas.html Feladatsorok: https://arato.inf.unideb.hu/baran.agnes/oktatas.html

Részletesebben

Mintavételes szabályozás mikrovezérlő segítségével

Mintavételes szabályozás mikrovezérlő segítségével Automatizálási Tanszék Mintavételes szabályozás mikrovezérlő segítségével Budai Tamás budai.tamas@sze.hu http://maxwell.sze.hu/~budait Tartalom Mikrovezérlőkről röviden Programozási alapismeretek ismétlés

Részletesebben

OOP. Alapelvek Elek Tibor

OOP. Alapelvek Elek Tibor OOP Alapelvek Elek Tibor OOP szemlélet Az OOP szemlélete szerint: a valóságot objektumok halmazaként tekintjük. Ezen objektumok egymással kapcsolatban vannak és együttműködnek. Program készítés: Absztrakciós

Részletesebben

Kifejezések. Kozsik Tamás. December 11, 2016

Kifejezések. Kozsik Tamás. December 11, 2016 Kifejezések Kozsik Tamás December 11, 2016 Kifejezés versus utasítás C/C++: kifejezés plusz pontosvessző: utasítás kiértékeli a kifejezést jellemzően: mellékhatása is van például: értékadás Ada: n = 5;

Részletesebben

Információk. Ismétlés II. Ismétlés. Ismétlés III. A PROGRAMOZÁS ALAPJAI 2. Készítette: Vénné Meskó Katalin. Algoritmus. Algoritmus ábrázolása

Információk. Ismétlés II. Ismétlés. Ismétlés III. A PROGRAMOZÁS ALAPJAI 2. Készítette: Vénné Meskó Katalin. Algoritmus. Algoritmus ábrázolása 1 Információk 2 A PROGRAMOZÁS ALAPJAI 2. Készítette: Vénné Meskó Katalin Elérhetőség mesko.katalin@tfk.kefo.hu Fogadóóra: szerda 9:50-10:35 Számonkérés időpontok Április 25. 9 00 Május 17. 9 00 Június

Részletesebben

Karakterkészlet. A kis- és nagybetűk nem különböznek, a sztringliterálok belsejét leszámítva!

Karakterkészlet. A kis- és nagybetűk nem különböznek, a sztringliterálok belsejét leszámítva! A PL/SQL alapelemei Karakterkészlet Az angol ABC kis- és nagybetűi: a-z, A-Z Számjegyek: 0-9 Egyéb karakterek: ( ) + - * / < > =! ~ ^ ; :. ' @ %, " # $ & _ { }? [ ] Szóköz, tabulátor, kocsivissza A kis-

Részletesebben

1. Jelölje meg az összes igaz állítást a következők közül!

1. Jelölje meg az összes igaz állítást a következők közül! 1. Jelölje meg az összes igaz állítást a következők közül! a) A while ciklusban a feltétel teljesülése esetén végrehajtódik a ciklusmag. b) A do while ciklusban a ciklusmag után egy kilépési feltétel van.

Részletesebben

Szoftvertervezés és -fejlesztés I.

Szoftvertervezés és -fejlesztés I. Szoftvertervezés és -fejlesztés I. Operátorok Vezérlési szerkezetek Gyakorlás 1 Hallgatói Tájékoztató A jelen bemutatóban található adatok, tudnivalók és információk a számonkérendő anyag vázlatát képezik.

Részletesebben

1. előadás. Lineáris algebra numerikus módszerei. Hibaszámítás Számábrázolás Kerekítés, levágás Klasszikus hibaanalízis Abszolút hiba Relatív hiba

1. előadás. Lineáris algebra numerikus módszerei. Hibaszámítás Számábrázolás Kerekítés, levágás Klasszikus hibaanalízis Abszolút hiba Relatív hiba Hibaforrások Hiba A feladatok megoldása során különféle hibaforrásokkal találkozunk: Modellhiba, amikor a valóságnak egy közelítését használjuk a feladat matematikai alakjának felírásához. (Pl. egy fizikai

Részletesebben

A C programozási nyelv I. Bevezetés

A C programozási nyelv I. Bevezetés A C programozási nyelv I. Bevezetés Miskolci Egyetem Általános Informatikai Tanszék A C programozási nyelv I. (bevezetés) CBEV1 / 1 A C nyelv története Dennis M. Ritchie AT&T Lab., 1972 rendszerprogramozás,

Részletesebben

Programozás II. 2. Dr. Iványi Péter

Programozás II. 2. Dr. Iványi Péter Programozás II. 2. Dr. Iványi Péter 1 C++ Bjarne Stroustrup, Bell Laboratórium Első implementáció, 1983 Kezdetben csak precompiler volt C++ konstrukciót C-re fordította A kiterjesztés alapján ismerte fel:.cpp.cc.c

Részletesebben

A C programozási nyelv I. Bevezetés

A C programozási nyelv I. Bevezetés A C programozási nyelv I. Bevezetés Miskolci Egyetem Általános Informatikai Tanszék A C programozási nyelv I. (bevezetés) CBEV1 / 1 A C nyelv története Dennis M. Ritchie AT&T Lab., 1972 rendszerprogramozás,

Részletesebben

Adattípusok, vezérlési szerkezetek. Informatika Szabó Adrienn szeptember 14.

Adattípusok, vezérlési szerkezetek. Informatika Szabó Adrienn szeptember 14. Informatika 1 2011 Második előadás, vezérlési szerkezetek Szabó Adrienn 2011. szeptember 14. Tartalom Algoritmusok, vezérlési szerkezetek If - else: elágazás While ciklus For ciklus Egyszerű típusok Összetett

Részletesebben

Számítástechnika I. BMEKOKAA152 BMEKOKAA119 Infokommunikáció I. BMEKOKAA606. Dr. Bécsi Tamás 2. előadás

Számítástechnika I. BMEKOKAA152 BMEKOKAA119 Infokommunikáció I. BMEKOKAA606. Dr. Bécsi Tamás 2. előadás Számítástechnika I. BMEKOKAA152 BMEKOKAA119 Infokommunikáció I. BMEKOKAA606 Dr. Bécsi Tamás 2. előadás Console I/O bővebben Lásd mintaprogram 2015.09.21. Számítástechnika I. 2. Előadás 2 Számábrázolásról

Részletesebben

Felvételi tematika INFORMATIKA

Felvételi tematika INFORMATIKA Felvételi tematika INFORMATIKA 2016 FEJEZETEK 1. Természetes számok feldolgozása számjegyenként. 2. Számsorozatok feldolgozása elemenként. Egydimenziós tömbök. 3. Mátrixok feldolgozása elemenként/soronként/oszloponként.

Részletesebben

1. Egyszerű (primitív) típusok. 2. Referencia típusok

1. Egyszerű (primitív) típusok. 2. Referencia típusok II. A Java nyelv eszközei 1. Milyen eszközöket nyújt a Java a programozóknak Korábban már említettük, hogy a Java a C nyelvből alakult ki, ezért a C, C++ nyelvben járatos programozóknak nem fog nehézséget

Részletesebben

A C# PROGRAMOZÁSI NYELV

A C# PROGRAMOZÁSI NYELV A C# PROGRAMOZÁSI NYELV 2010.02.23. Bevezetés C# nyelv jellemzői 2 Kis és NAGY betű érzékeny Minden utasítást pontos vessző zár. Utasítás zárójel a:,. .NET Framework keretrendszerek 3 Microsoft.NET Framework

Részletesebben

Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések. 1. Mi a programozás?

Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések. 1. Mi a programozás? Bevezetés Kinek szól a könyv? A könyv témája A könyv felépítése Mire van szükség a könyv használatához? A könyvben használt jelölések Forráskód Hibajegyzék p2p.wrox.com xiii xiii xiv xiv xvi xvii xviii

Részletesebben

Pásztor Attila. Algoritmizálás és programozás tankönyv az emeltszintű érettségihez

Pásztor Attila. Algoritmizálás és programozás tankönyv az emeltszintű érettségihez Pásztor Attila Algoritmizálás és programozás tankönyv az emeltszintű érettségihez 3. ADATTÍPUSOK...26 3.1. AZ ADATOK LEGFONTOSABB JELLEMZŐI:...26 3.2. ELEMI ADATTÍPUSOK...27 3.3. ÖSSZETETT ADATTÍPUSOK...28

Részletesebben

A programozás alapjai

A programozás alapjai A programozás alapjai Változók A számítógép az adatokat változókban tárolja A változókat alfanumerikus karakterlánc jelöli. A változóhoz tartozó adat tipikusan a számítógép memóriájában tárolódik, szekvenciálisan,

Részletesebben

5.1.4 Laborgyakorlat: A Windows számológép használata hálózati címeknél

5.1.4 Laborgyakorlat: A Windows számológép használata hálózati címeknél 5.1.4 Laborgyakorlat: A Windows számológép használata hálózati címeknél Célok Átkapcsolás a Windows Számológép két működési módja között. A Windows Számológép használata a decimális (tízes), a bináris

Részletesebben

5. Fejezet : Lebegőpontos számok. Lebegőpontos számok

5. Fejezet : Lebegőpontos számok. Lebegőpontos számok 5. Fejezet : Lebegőpontos The Architecture of Computer Hardware and Systems Software: An InformationTechnology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003 Wilson Wong, Bentley College Linda

Részletesebben

AWK programozás, minták, vezérlési szerkezetek

AWK programozás, minták, vezérlési szerkezetek 10 AWK programozás, minták, vezérlési szerkezetek AWK futtatási módok AWK parancs, közvetlen programkódmegadás: awk 'PROGRAMKÓD' FILE példa: ls -l awk '{print $1, $5}' a programkód helyére minden indentálás

Részletesebben

Bevezetés az informatikába gyakorló feladatok Utoljára módosítva:

Bevezetés az informatikába gyakorló feladatok Utoljára módosítva: Tartalom 1. Számrendszerek közti átváltás... 2 1.1. Megoldások... 4 2. Műveletek (+, -, bitműveletek)... 7 2.1. Megoldások... 8 3. Számítógépes adatábrázolás... 12 3.1. Megoldások... 14 A gyakorlósor lektorálatlan,

Részletesebben

Java II. I A Java programozási nyelv alapelemei

Java II. I A Java programozási nyelv alapelemei Java2 / 1 Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2009. 02. 09. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve

Részletesebben

C++ referencia. Izsó Tamás február 17. A C++ nyelvben nagyon sok félreértés van a referenciával kapcsolatban. A Legyakoribb hibák:

C++ referencia. Izsó Tamás február 17. A C++ nyelvben nagyon sok félreértés van a referenciával kapcsolatban. A Legyakoribb hibák: C++ referencia Izsó Tamás 2017. február 17. 1. Bevezetés A C++ nyelvben nagyon sok félreértés van a referenciával kapcsolatban. A Legyakoribb hibák: Sokan összetévesztik a pointerrel. Keveset alkalmazzák

Részletesebben

Osztályok. 4. gyakorlat

Osztályok. 4. gyakorlat Osztályok 4. gyakorlat Az osztály fogalma Az objektumok formai leírása, melyek azonos tulajdonsággal és operációkkal rendelkeznek. Osztályból objektum készítését példányosításnak nevezzük. Minden objektum

Részletesebben

Programozás C- és Matlab nyelven C programozás kurzus BMEKOKAM603 Előfeldolgozó rendszer Tömbök. Dr. Bécsi Tamás 4. Előadás

Programozás C- és Matlab nyelven C programozás kurzus BMEKOKAM603 Előfeldolgozó rendszer Tömbök. Dr. Bécsi Tamás 4. Előadás Programozás C- és Matlab nyelven C programozás kurzus BMEKOKAM603 Előfeldolgozó rendszer Tömbök Dr. Bécsi Tamás 4. Előadás A?: operátor Nézzük meg a következő kifejezést: if (a>b) z=a; else z=b; Ez felírható

Részletesebben

5. Fejezet : Lebegőpontos számok

5. Fejezet : Lebegőpontos számok 5. Fejezet : Lebegőpontos The Architecture of Computer Hardware and Systems Software: An Information Technology Approach 3. kiadás, Irv Englander John Wiley and Sons 2003 Wilson Wong, Bentley College Linda

Részletesebben

Informatika terméktervezőknek

Informatika terméktervezőknek Informatika terméktervezőknek C# alapok Névterület (namespace) using Osztály (class) és Obejtumok Metódus (function, procedure, method) main() static void string[] arg Szintaxis // /* */ \n \t Névadások

Részletesebben

AWK programozás Bevezetés

AWK programozás Bevezetés 09 AWK programozás Bevezetés AWK adatvezérelt szkriptnyelv text processing, adat kiterjesztés, tagolt adatok automatizált soronkénti feldolgozása a forrásállományt soronként beolvassa és feldolgozhatóvá

Részletesebben

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós március 3. Széchenyi István Egyetem, Gy r

Programozás. (GKxB_INTM021) Dr. Hatwágner F. Miklós március 3. Széchenyi István Egyetem, Gy r Programozás (GKxB_INTM021) Széchenyi István Egyetem, Gy r 2018. március 3. Függvények Mi az a függvény (function)? Programkód egy konkrét, azonosítható, paraméterezhet, újrahasznosítható blokkja Miért

Részletesebben

LEGO robotok. XII. rész

LEGO robotok. XII. rész LEGO robotok XII. rész III.1.22. Változók és konstansok A változó fogalma a matematikában egy értelmezési tartománnyal rendelkező, ebből bármilyen értéket felvehető objektum, melynek értéke logikailag

Részletesebben

Bevezetés a programozásba

Bevezetés a programozásba Bevezetés a programozásba 1. Előadás Bevezetés, kifejezések http://digitus.itk.ppke.hu/~flugi/ Egyre precízebb A programozás természete Hozzál krumplit! Hozzál egy kiló krumplit! Hozzál egy kiló krumplit

Részletesebben

1. Gyakorlat. Rövid elméleti összefoglaló. <tárolási osztály>típus <típus > változónév <= kezdőérték><, >;

1. Gyakorlat. Rövid elméleti összefoglaló. <tárolási osztály>típus <típus > változónév <= kezdőérték><, >; Rövid elméleti összefoglaló 1. Gyakorlat A C++ nyelv hatékony, általános célú programozási nyelv, amely hagyományos fejlesztőeszközként és objektum-orientált programozási nyelvként egyaránt használható.

Részletesebben

5-6. ea Created by mrjrm & Pogácsa, frissítette: Félix

5-6. ea Created by mrjrm & Pogácsa, frissítette: Félix 2. Adattípusonként különböző regisztertér Célja: az adatfeldolgozás gyorsítása - különös tekintettel a lebegőpontos adatábrázolásra. Szorzás esetén karakterisztika összeadódik, mantissza összeszorzódik.

Részletesebben

Eljárások és függvények

Eljárások és függvények Eljárások és függvények Jegyzet Összeállította: Faludi Anita 2012. Bevezetés Ez a jegyzet elsősorban azoknak a diákoknak készült, akiket tanítok, ezért a jegyzet erőteljesen hiányos. Az olvasó egy percig

Részletesebben

Egyes esetekben e fejezet keretében készítjük el a Tartalomjegyzéket is, melynek technikai megvalósításáról majd az 5.6.6. fejezetben olvashat.

Egyes esetekben e fejezet keretében készítjük el a Tartalomjegyzéket is, melynek technikai megvalósításáról majd az 5.6.6. fejezetben olvashat. Szövegszerkesztés 1. Bevezetés Ebben a modulban a szövegszerkesztési szabályokat kívánjuk bemutatni. Feltételezzük, az olvasó már ismer legalább egy szövegszerkesztő programot, így annak teljes körű bemutatására

Részletesebben

Algoritmizálás és adatmodellezés tanítása 1. előadás

Algoritmizálás és adatmodellezés tanítása 1. előadás Algoritmizálás és adatmodellezés tanítása 1. előadás Algoritmus-leíró eszközök Folyamatábra Irányított gráf, amely csomópontokból és őket összekötő élekből áll, egyetlen induló és befejező éle van, az

Részletesebben

Bevezetés a programozásba I.

Bevezetés a programozásba I. Bevezetés a programozásba I. 5. gyakorlat Surányi Márton PPKE-ITK 2010.10.05. C++ A C++ egy magas szint programozási nyelv. A legels változatot Bjarne Stroutstrup dolgozta ki 1973 és 1985 között, a C nyelvb

Részletesebben

BASH SCRIPT SHELL JEGYZETEK

BASH SCRIPT SHELL JEGYZETEK BASH SCRIPT SHELL JEGYZETEK 1 TARTALOM Paraméterek... 4 Változók... 4 Környezeti változók... 4 Szűrők... 4 grep... 4 sed... 5 cut... 5 head, tail... 5 Reguláris kifejezések... 6 *... 6 +... 6?... 6 {m,n}...

Részletesebben

HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport

HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport 10-es Keressünk egy egész számokat tartalmazó négyzetes mátrixban olyan oszlopot, ahol a főátló alatti elemek mind nullák! Megolda si terv: Specifika cio : A = (mat: Z n m,ind: N, l: L) Ef =(mat = mat`)

Részletesebben

Aritmetikai utasítások I.

Aritmetikai utasítások I. Aritmetikai utasítások I. Az értékadó és aritmetikai utasítások során a címzési módok különböző típusaira látunk példákat. A 8086/8088-as mikroprocesszor memóriája és regiszterei a little endian tárolást

Részletesebben

Rekurzió. Dr. Iványi Péter

Rekurzió. Dr. Iványi Péter Rekurzió Dr. Iványi Péter 1 Függvényhívás void f3(int a3) { printf( %d,a3); } void f2(int a2) { f3(a2); a2 = (a2+1); } void f1() { int a1 = 1; int b1; b1 = f2(a1); } 2 Függvényhívás void f3(int a3) { printf(

Részletesebben

C++ programozási nyelv

C++ programozási nyelv C++ programozási nyelv Gyakorlat - 13. hét Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. december A C++ programozási nyelv Soós Sándor 1/10 Tartalomjegyzék Objektumok

Részletesebben

Szoftvertechnológia alapjai Java előadások

Szoftvertechnológia alapjai Java előadások Szoftvertechnológia alapjai Java előadások Förhécz András, doktorandusz e-mail: fandrew@mit.bme.hu tárgy honlap: http://home.mit.bme.hu/~fandrew/szofttech_hu.html A mai előadás tartalma: Miért pont Java?

Részletesebben

3. gyakorlat. Kettes számrendszer: {0, 1} Tízes számrendszer: {0, 1, 2,..., 9} 16-os (hexadecimális számrendszer): {0, 1, 2,..., 9, A, B, C, D, E, F}

3. gyakorlat. Kettes számrendszer: {0, 1} Tízes számrendszer: {0, 1, 2,..., 9} 16-os (hexadecimális számrendszer): {0, 1, 2,..., 9, A, B, C, D, E, F} 3. gyakorlat Számrendszerek: Kettes számrendszer: {0, 1} Tízes számrendszer: {0, 1, 2,..., 9} 16-os (hexadecimális számrendszer): {0, 1, 2,..., 9, A, B, C, D, E, F} Alaki érték: 0, 1, 2,..., 9,... Helyi

Részletesebben

Vezérlési szerkezetek

Vezérlési szerkezetek Vezérlési szerkezetek Szelekciós ok: if, else, switch If Segítségével valamely ok végrehajtását valamely feltétel teljesülése esetén végezzük el. Az if segítségével valamely tevékenység () végrehajtását

Részletesebben

Amit a törtekről tudni kell Minimum követelményszint

Amit a törtekről tudni kell Minimum követelményszint Amit a törtekről tudni kell Minimum követelményszint Fontos megjegyzés: A szabályoknak nem a pontos matematikai meghatározását adtuk. Helyettük a gyakorlatban használható, egyszerű megfogalmazásokat írtunk.

Részletesebben

SZÁMÍTÁSOK A TÁBLÁZATBAN

SZÁMÍTÁSOK A TÁBLÁZATBAN SZÁMÍTÁSOK A TÁBLÁZATBAN Az Excelben az egyszerű adatok bevitelén kívül számításokat is végezhetünk. Ezeket a cellákba beírt képletek segítségével oldjuk meg. A képlet: olyan egyenlet, amely a munkalapon

Részletesebben

SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS

SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS 2.ELŐADÁS A VB programozási nyelv Az Excel programozása 2 A VB programozási nyelv Adattípusok Adatok kezelése Vezérlőszerkezetek Adattípusok és műveletek Egész adattípusok

Részletesebben

Algoritmusok Tervezése. 4. Előadás Visual Basic 1. Dr. Bécsi Tamás

Algoritmusok Tervezése. 4. Előadás Visual Basic 1. Dr. Bécsi Tamás Algoritmusok Tervezése 4. Előadás Visual Basic 1. Dr. Bécsi Tamás Bevezetés A BASIC (Beginner s All-purpose Symbolic Instruction Code) programnyelvet oktatási célokra hozták létre 1964-ben. Az általános

Részletesebben

A függvény kód szekvenciáját kapcsos zárójelek közt definiáljuk, a { } -ek közti részt a Bash héj kód blokknak (code block) nevezi.

A függvény kód szekvenciáját kapcsos zárójelek közt definiáljuk, a { } -ek közti részt a Bash héj kód blokknak (code block) nevezi. Függvények 1.Függvények...1 1.1.A függvény deníció szintaxisa... 1..Függvények érték visszatérítése...3 1.3.Környezettel kapcsolatos kérdések...4 1.4.Lokális változók használata...4 1.5.Rekurzív hívások...5.kód

Részletesebben

7. fejezet: Mutatók és tömbök

7. fejezet: Mutatók és tömbök 7. fejezet: Mutatók és tömbök Minden komolyabb programozási nyelvben vannak tömbök, amelyek gondos kezekben komoly fegyvert jelenthetnek. Először is tanuljunk meg tömböt deklarálni! //Tömbök használata

Részletesebben

Pénzügyi algoritmusok

Pénzügyi algoritmusok Pénzügyi algoritmusok A C++ programozás alapjai Az Integrált Fejlesztői Környezet C++ alapok Az Integrált Fejlesztői Környezet Visual Studio 2013 Community Edition Kitekintés: fordítás Preprocesszor Fordító

Részletesebben

Bevezetés a programozásba. 8. Előadás: Függvények 2.

Bevezetés a programozásba. 8. Előadás: Függvények 2. Bevezetés a programozásba 8. Előadás: Függvények 2. ISMÉTLÉS Helló #include using namespace std; int main() cout

Részletesebben

Programozás II. 2. gyakorlat Áttérés C-ről C++-ra

Programozás II. 2. gyakorlat Áttérés C-ről C++-ra Programozás II. 2. gyakorlat Áttérés C-ről C++-ra Tartalom Új kommentelési lehetőség Változók deklarációjának helye Alapértelmezett függvényparaméterek Névterek I/O műveletek egyszerűsödése Logikai adattípus,

Részletesebben

Bevezetés a programozásba I 10. gyakorlat. C++: alprogramok deklarációja és paraméterátadása

Bevezetés a programozásba I 10. gyakorlat. C++: alprogramok deklarációja és paraméterátadása Pázmány Péter Katolikus Egyetem Információs Technológiai Kar Bevezetés a programozásba I 10. gyakorlat C++: alprogramok deklarációja és paraméterátadása 2011.11.22. Giachetta Roberto groberto@inf.elte.hu

Részletesebben

Alapkapuk és alkalmazásaik

Alapkapuk és alkalmazásaik Alapkapuk és alkalmazásaik Bevezetés az analóg és digitális elektronikába Szabadon választható tárgy Összeállította: Farkas Viktor Irányítás, irányítástechnika Az irányítás esetünkben műszaki folyamatok

Részletesebben

Elemi matematika szakkör

Elemi matematika szakkör Elemi matematika szakkör Kolozsvár, 2015. október 5. 1.1. Feladat. Egy pozitív egész számot K tulajdonságúnak nevezünk, ha számjegyei nullától különböznek és nincs két azonos számjegye. Határozd meg az

Részletesebben

SQL*Plus. Felhasználók: SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP (dolgozó), DEPT (osztály) "közönséges" felhasználók

SQL*Plus. Felhasználók: SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP (dolgozó), DEPT (osztály) közönséges felhasználók SQL*Plus Felhasználók: SYS: rendszergazda SCOTT: demonstrációs adatbázis, táblái: EMP dolgozó), DEPT osztály) "közönséges" felhasználók Adatszótár: metaadatokat tartalmazó, csak olvasható táblák táblanév-prefixek:

Részletesebben

KOVÁCS BÉLA, MATEMATIKA I.

KOVÁCS BÉLA, MATEMATIKA I. KOVÁCS BÉLA, MATEmATIkA I. 1 I. HALmAZOk 1. JELÖLÉSEk A halmaz fogalmát tulajdonságait gyakran használjuk a matematikában. A halmazt nem definiáljuk, ezt alapfogalomnak tekintjük. Ez nem szokatlan, hiszen

Részletesebben

1.1. A forrásprogramok felépítése Nevek és kulcsszavak Alapvető típusok. C programozás 3

1.1. A forrásprogramok felépítése Nevek és kulcsszavak Alapvető típusok. C programozás 3 Darvay Zsolt Típusok és nevek a forráskódban Állandók és változók Hatókörök és az előfeldolgozó Bevitel és kivitel Kifejezések Utasítások Mutatók Függvények Struktúrák és típusok Állománykezelés C programozás

Részletesebben

Gyakorló feladatok Gyakorló feladatok

Gyakorló feladatok Gyakorló feladatok Gyakorló feladatok előző foglalkozás összefoglalása, gyakorlató feladatok a feltételes elágazásra, a while ciklusra, és sokminden másra amit eddig tanultunk Változók elnevezése a változók nevét a programozó

Részletesebben

A C programozási nyelv II. Utasítások. A függvény.

A C programozási nyelv II. Utasítások. A függvény. A C programozási nyelv II. Utasítások. A függvény. Miskolci Egyetem Általános Informatikai Tanszék A C programozási nyelv II (Utasítások, fuggvények) CBEV2 / 1 Kifejezés utasítás Kifejezés utasítás, blokk

Részletesebben