MQL4 könyv    Kereskedelmi műveletek programozása    Megbízások  zárása és törlése

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

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:

Feladat

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

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:

Figyelem!

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

Példa

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.
//--------------------------------------------------------------------------------- 1 --
int start() // Special function 'start'
 {
 string Symb=Symbol(); // Symbol
 double Dist=1000000.0; // Presetting
 int Real_Order=-1; // No market orders yet
 double Win_Price=WindowPriceOnDropped(); // The script is dropped here
//-------------------------------------------------------------------------------- 2 --
 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:
 //----------------------------------------------------------------------- 3 --
 if (OrderSymbol()!= Symb) continue; // Symbol is not ours
 int Tip=OrderType(); // Order type
 if (Tip>1) continue; // Pending order 
 //----------------------------------------------------------------------- 4 --
 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
 }
 //----------------------------------------------------------------------- 5 --
 } //End of order analysis
 } //End of order searching
//-------------------------------------------------------------------------------- 6 --
 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 
 }
 //-------------------------------------------------------------------------- 7 --
 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
 Text="Sell "; // Text for Sell
 }
 Alert("Attempt to close ",Text," ",Ticket,". Awaiting response..");
 bool Ans=OrderClose(Ticket,Lot,Price_Cls,2);// Order closing
 //-------------------------------------------------------------------------- 8 --
 if (Ans==true) // Got it! :)
 {
 Alert ("Closed order ",Text," ",Ticket);
 break; // Exit closing cycle
 }
 //-------------------------------------------------------------------------- 9 --
 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
 }
//------------------------------------------------------------------------------- 10 --
 Alert ("The script has finished operations -----------------------------");
 return; // Exit start()
 }
//------------------------------------------------------------------------------- 11 --

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 4372930 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, 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ó) a legkö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 kurzorhoz bá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 a start() 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.


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:

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.

Példa

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.
//-------------------------------------------------------------------------------- 1 --
int start() // Special function 'start'
 {
 string Symb=Symbol(); // Symbol
 double Dist=1000000.0; // Presetting
 int Limit_Stop=-1; // No pending orders yet
 double Win_Price=WindowPriceOnDropped(); // The script is dropped here
//-------------------------------------------------------------------------------- 2 --
 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:
 //----------------------------------------------------------------------- 3 --
 if (OrderSymbol()!= Symb) continue; // Symbol is not ours
 int Tip=OrderType(); // Order type
 if (Tip>2) continue; // Market order 
 //----------------------------------------------------------------------- 4 --
 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
//-------------------------------------------------------------------------------- 5 --
 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'
 case 5: Text= "SellStop "; // Text for SellStop
 break; // Exit 'switch'
 }
//-------------------------------------------------------------------------------- 6 --
 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 
 }
 //-------------------------------------------------------------------------- 7 --
 Alert("Attempt to delete ",Text," ",Ticket,". Awaiting response..");
 bool Ans=OrderDelete(Ticket); // Deletion of the order
 //-------------------------------------------------------------------------- 8 --
 if (Ans==true) // Got it! :)
 {
 Alert ("Deleted order ",Text," ",Ticket);
 break; // Exit closing cycle
 }
 //-------------------------------------------------------------------------- 9 --
 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
 }
//------------------------------------------------------------------------------- 10 --
 Alert ("The script has finished operations -----------------------------");
 return; // Exit start()
 }
//------------------------------------------------------------------------------- 11 --

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

145. 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 1.2500-nál adtunk és a fagyott zóna egyenlő 10 ponttal, ez azt jelenti, ha piaci ár az 1.2490- 1.2510 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:


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

Példa

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.
//--------------------------------------------------------------- 1 --
int start()                                    // Special function 'start'
  {
  string Symb=Symbol();                        // Symbol
  double Dist=1000000.0;                      // Presetting
//--------------------------------------------------------------- 2 --
  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:
            //--------------------------------------------------- 3 --
            if (OrderSymbol()!= Symb) continue; // Symbol is not ours
            int Tip=OrderType();                // Order type
            if (Tip>1) continue;                // Pending order  
            //--------------------------------------------------- 4 --
            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
      //--------------------------------------------------------- 5 --
      if (Hedg_Buy<0 || Hedg_Sell<0)            // If no order available..
        {                                      // ..of some type
        Alert("All opposite orders are closed :)");// Message  
        return;                                // Exit start()
        }
      //--------------------------------------------------------- 6 --
      while(true)                              // Closing cycle 
        {
        //------------------------------------------------------ 7 --
        Alert("Attempt to close by. Awaiting response..");
        bool Ans=OrderCloseBy(Ticket_Buy,Ticket_Sell);// Закрытие 
        //------------------------------------------------------ 8 --
        if (Ans==true)                        // Got it! :)
          {
            Alert ("Performed closing by.");
            break;                              // Exit closing cycle
          }
        //------------------------------------------------------ 9 --
        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 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
//-------------------------------------------------------------- 10 --
  }                                            // 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.

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.

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 778594, 1lot mérettel és Sell 778595, 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 778597. Ezután a program kiválasztotta a Buy 778592 és Sell 778593, 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 778596 0.3 lotos megbízás, és a script által nyitott Buy 778597 0.2 lotos megbízás. 100. á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.