MQL4 programozás: pozíciók zárása

Pozíciók zárása

Henrik nevű kedves olvasónk e-mailben érkezett kérésének apropóján ez a poszt a pozíciók zárásáról fog szólni – programozói szemszögből.

A konkrét kérdés arra vonatkozott, hogy hogyan lehet kiszűrni és lezárni a vesztes pozíciókat.

Mielőtt hozzákezdenénk, mindenképpen javaslom a korábbi, “Utolsó pozíció lekérdezése” című cikkem elolvasását. A cikkben leírom a ticket számok lényegét, alapszintű kezelését – erre épít a jelenlegi cikk is, ezért e nélkül bizonyos részek nem, vagy nem teljesen lesznek érthetők.

Az alapszintű probléma, amivel mindenki szembesülni fog: a pozíciók indexei folyamatosan változnak, ezért abban a pillanatban amikor sikerrel zárunk egy pozíciót, a többi pozíció addig ismert indexe megváltozik. A for ciklusok éppen ezért csak megkötésekkel használhatóak!

Módszerek

Természetesen – ez a programozás világában majd’ minden problémára igaz – több lehetőségünk van megoldani a fenti problémát. Az általam felsoroltakon kívül is biztosan vannak még módszerek – én a számomra beváltakat írom le, ezek:

  1. for ciklus segítségével a zárni kívánt pozíciók összeszámolása, később egy while ciklusba ágyazott for ciklus használata a záráshoz
  2. for ciklus segítségével a zárni kívánt pozíciók ticket számainak tömbbe mentése, később a tömb feldolgozása szintén for ciklussal

Jómagam korábban az első változatot használtam, manapság azonban inkább a második megoldás a nyerő. Nem véletlen, hiszen a második megoldás gyakorlatilag az objektumok és globális változók törlése során is sikerrel használható, mivel az indexelési probléma ott is teljesen ugyanaz: ha például törlünk egy objektumot, akkor az objektumok indexszámai megváltoznak és a for ciklusunkban bajba kerülhetünk.

Jelen leírásban csak a második módszert fejtem ki, az elsőről pár szóban írok csak.

1. módszer

Ennél a módszernél először – akár a korábbi cikkben leírt módon – megszámoljuk, hogy hány pozíciót szeretnénk zárni. Tegyük fel, hogy az összes veszteséges buy pozíciót szeretnénk lezárni! A korábbi cikkben ismertetett módon megszámoljuk, hány veszteséges buy pozíció van; egy következő, while ciklussal pedig egészen addig pörgetjük újra és újra a for ciklust, ameddig nincs az elvárt számú lezárt buy pozíciónk. Amikor elérjük a megfelelő darabszámot, megszakítjuk a while ciklust, ezzel akadályozva meg azt, hogy a végtelenségig fusson. Természetesen minden ciklust – legyen az for vagy while – érdemes és ésszerű korlátozni, hogy ha programozási hibát vétünk, akkor ne próbáljon például elszámolni a végtelenig.

2. módszer

Ez a módszer abban különbözik az előzőtől, hogy a zárási folyamatot – ami mondjuk egy egy darab pozíciót zárni képes függvény – pontosan annyiszor hívjuk meg, mint ahány, a korábbi feltételre illeszkedő pozíciónk van. A pozíciókat – illetve azok ticket számát – egy tömbben rögzítjük, amelyet rögtön a szűrés után fel is dolgozunk majd.

Először is, keressük meg a zárni kívánt pozíciókat! Ezt az instrumentumra, a magic számra és a veszteségességre szűrve tudjuk megtenni.

Az OrderProfit() függvény visszaadja az éppen kiválasztott pozíció profitját – érdemes ehhez hozzáadni az OrderSwap() illetve OrderCommission() függvények visszatérési értékét is, hiszen a két érték együtt adja meg a nettó profitunkat. A példában csak az OrderProfit() függvényt használom.

Nyilván mindkét módszer esetén illik figyelni arra, hogy ha a zárási műveleteket valami kapitális, általunk nem befolyásolt tény szakítja meg (pl.: a kereskedés nincs engedélyezve), akkor ne próbálkozzunk a végtelenségig.

   for (int k = 0; k < OrdersTotal(); k++) {
 
      if (OrderSelect(k, SELECT_BY_POS)) {
 
         if (OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber && OrderProfit() < 0) {
            Print ("Veszteséges pozíció -> #", OrderTicket());
         }
      }
   }

A fenti kód kiírja a terminál Expertek fülére az aktuális vesztes pozíciók ticket számait.

A következő feladat: rögzítsük ideiglenes adatként ezeket a ticket számokat! A megoldáshoz egy tömböt fogunk használni. Hozzunk hát létre egyet!

Mivel nem tudjuk, hogy pontosan hány elemű tömbre lesz szükségünk, egyelőre üresen hozzuk létre.

   int
      poziciok[];

A kiíratás mellett az alábbi kódban beillesztjük a tömbbe az egyes ticket számokat, ügyelve az alábbiakra:

  1. egy tömbbe mindig csak akkor tudunk adatot illeszteni, ha legalább akkora, mint ahány adatot be szeretnénk szúrni. Az üres tömbünk jelenleg nulla elemszámmal rendelkezik, ezért az első beszúrás előtt megnöveljük az elemszámát eggyel.
  2. ezt az ellenőrzést a jövőben is minden beszúrás előtt ellenőrizni kell; erre létrehozhatunk egy egyszerű függvényt is, hogy ne kelljen mindig újra és újra a szokásos tömbkezelési manővereket kódba illesztenünk.

Az egydimenziós tömbünk aktuális méretét az ArraySize() lekérdezni, míg a megfelelő elemszámra történő átméretezést az ArrayResize() függvénnyel tudjuk megoldani.

Természetesen itt is érvényes a programozási nyelvekben szokásos nullától induló indexelés: vagyis ha egy tömbben 1 elemünk van, akkor annak az indexe nulla lesz.

Ebből egyértelműen következik, hogy ha szeretnénk még egy elemmel bővíteni majd az egy elemet tartalmazó tömbünket, akkor 2 darabra kell átméreteznünk; ekkor a nullás indexű első elem érintetlen marad, létrejön egy üres 1-es indexű elem, amibe belepakolhatjuk az új adatunkat. Az így frissült két elemű tömb indexeire a 0 és 1 segítségével hivatkozhatunk majd. A feladat során valójában persze dinamikusan, azaz változóval fogjuk a hivatkozást megoldani!

Fontos, hogy:

  • a tömb üres elemeinek alapértéke mindig nulla!
  • ha kisebbé zsugorítunk egy tömböt, akkor az új darabszámon felüli adatok elvesznek!

A fentiek egy összefoglaló, egyszerű példakódban:

   int
      poziciok[];
 
   Print ("A tömb elemszáma = ", ArraySize(poziciok));
   // eredmény: A tömb elemszáma = 0
 
   ArrayResize(poziciok, 1);
   // átméretezés 1 elemű tömbbé
 
   poziciok[0] = 2013;
   // az egyetlen, nullás indexű elembe beszúrunk egy értéket
 
   Print ("A tömb elemszáma = ", ArraySize(poziciok));
   // eredmény: A tömb elemszáma = 1
 
   Print ("A nullás index értéke = ", poziciok[0]);
   // eredmény: A nullás index értéke = 2013
 
   ArrayResize(poziciok, 2);
   // átméretezés 2 elemű tömbbé
 
   poziciok[1] = 2014;
   // a második indexű (1) elemet feltöltjük adattal
 
   Print ("A tömb elemszáma = ", ArraySize(poziciok));
   // eredmény: A tömb elemszáma = 2
 
   Print ("A nullás index értéke = ", poziciok[0], ", a második adat pedig = ", poziciok[1]);
   // eredmény: A nullás index értéke = 2013, a második adat pedig = 2014
 
   ArrayResize(poziciok, 1);
   // kisebbre zsugorítjuk a tömbünket...
 
   Print (poziciok[1]);
   // ...így elveszítjuk a korábbi adatunkat (2014)
 
   ArrayResize(poziciok, 2);
   // újra kettőre növeljük az elemszámot...
 
   Print (poziciok[1]);
   // ...az adatunk viszont nincs meg, alap értékként nullát kapunk vissza.

A tömbbe történő adatbeszúrás előtt tehát mindenképpen az aktuális elemszámot kell megnövelnünk eggyel. Ehhez először lekérdezzük a tömb aktuális méretét (m változó), majd + 1 hozzáadásával átméretezzük. Ezek után az m változó alapján feltöltjük adattal az új indexet, mely természetesen m – 1 lesz a nullától induló indexelés miatt.

A példakódban a könnyebb tesztelhetőség kedvéért az instrumentumon nyitva lévő összes pozíciót vesszük alapul, hogy ne kelljen mesterségesen létrehoznunk megfelelő magic számú pozíciókat és megvárnunk, míg veszteségbe fordulnak át.

   int
      m = 0,
      poziciok[];
 
   for (int k = 0; k < OrdersTotal(); k++) {
 
      if (OrderSelect(k, SELECT_BY_POS)) {
 
         if (OrderSymbol() == Symbol()) {
 
            m = ArraySize(poziciok);
 
            ArrayResize(poziciok, m + 1);
 
            poziciok[m] = OrderTicket();
         }
      }
   }
 
   Print ("A tömb elemszáma = ", ArraySize(poziciok));
   // eredmény: A tömb elemszáma = 2

Legyen legalább kettő nyitott pozíciónk! A kód első lefutása után a tömbben annyi darab elem lesz, ahány pozíciónk nyitva van az adott instrumentumon.

Fontos: a tömb definiálásával nem törlődik automatikusan annak tartalma! Egy minden tickben lefutó expert a fenti kód alapján folyamatosan és fáradhatatlanul növelné a tömb elemeinek számát, óriási káoszt okozva ezzel. Ez a probléma könnyen kikerülhető, ha minden tömbdefiniálás után az elemek számát nullára állítjuk. Így az esetleges korábbi indexek és azok tartalmai mindenképpen törlődnek.

Ha minden igaz, akkor megvannak az érintett pozíciók ticket számai – használjuk fel őket! Ezt az alábbi kóddal tudjuk megtenni. A kódot a korábban ismertetett kód után kell beszúrni!

   Print ("A tömb elemszáma = ", ArraySize(poziciok));
   // eredmény: A tömb elemszáma = 2
 
   for (k = 0; k < ArraySize(poziciok); k++) {
 
      Print ("index = ", k, ", adat = ", poziciok[k]);
   }
 
   // A tömb elemszáma = 2
   // index = 0, adat = 142099398
   // index = 1, adat = 142099402

Természetesen az elemek száma és a ticket mindenkinél számok különbözni fognak, de a lényeg remélhetőleg látható. Létrehoztunk egy for ciklust, amely nullától indul és a poziciok nevű tömb elemszámánál kisebb értékig tart. A példámban 0-tól indulunk, és egészen 1-ig megyünk – hiszen 1 az utolsó egész szám, amely kisebb mint 2. Az ArraySize(poziciok) értékét természetesen elrakhatjuk egy változóba is, hogy ne kelljen újra és újra lekérdezni.

Az adatokkal kapcsolatos beillesztő és kiírató kódrész teljes egészében:

   int
      m = 0,
      poziciok[],
      elemszam;
 
   ArrayResize(poziciok, 0);
   // a tömb elemeinek nullára állítása
 
   for (int k = 0; k < OrdersTotal(); k++) {
 
      if (OrderSelect(k, SELECT_BY_POS)) {
 
         if (OrderSymbol() == Symbol()) {
 
            m = ArraySize(poziciok);
 
            ArrayResize(poziciok, m + 1);
 
            poziciok[m] = OrderTicket();
         }
      }
   }
 
   elemszam = ArraySize(poziciok);
 
   Print ("A tömb elemszáma = ", elemszam);
   // eredmény: A tömb elemszáma = 2
 
   for (k = 0; k < elemszam; k++) {
 
      Print ("index = ", k, ", adat = ", poziciok[k]);
   }

Pozíciózárás

Lényegében a feladat első részével készen vagyunk: tudjuk, mely pozíciókat szeretnénk lezárni. A zárás egy egyszerűnek tűnő folyamat, amit sokan az egysoros OrderClose() függvénnyel akarnak letudni. Ez a hozzáállás több sebből vérzik: mi van, ha nem sikerül a pozíció zárása? Nem mehetünk automatikusan tovább, mintha mi sem történt volna!

A pozíciózárásnak – mint minden más folyamatnak – van egy protokollja, sémája: ki hogy nevezi. Nézzük meg, mit kéne kell tartalmazzon egy saját készítésű zárási függvény:

  1. pozíció ticket számának ellenőrzése
  2. pozíció létezésének ellenőrzése
  3. nyitva van még egyáltalán a pozíció?
  4. fagyási szint (freeze level) szabály van-e?
  5. megfelelő zárási ár lekérdezése
  6. sikeres volt a zárás?
  7. sikertelen zárás esetén az egész procedúra újrakezdése, amennyiben nem léptük túl a maximum próbálkozási számot

A lista elsőre túlzónak tűnhet, azonban a tapasztalat azt mutatja: igenis megéri foglalkozni a néha banálisnak tűnő ellenőrzésekkel is.

  1. A pozíció ticket számának ellenőrzése alatt mindösszesen annyit értek, hogy nézzük meg: nullánál nagyobb-e, vagy sem. Amennyiben igen, továbbhaladhatunk. Ha mégsem, akkor a programunk más részétől már eleve hibás vagy hiányzó adatot kaptunk. A vizsgálattal megkíméljük magunkat a nem létező ticket számmal kapcsolatos zárási próbálkozásoktól.
  2. Ellenőriznünk kell azt is, hogy ki tudjuk-e választani a pozíciót. Ezt az OrderSelect() függvénnyel tehetjük meg, amelynek visszatérési értéke true kell, hogy legyen. Amennyiben a függvény false értéket ad vissza, akkor valami gond van, és szintén hiába mennénk tovább.
  3. Elképzelhető, hogy egy másik program már zárta az adott pozíciót. Ebben az esetben hiába próbálkozunk a zárással, hiszen már nincs mit lezárni. Az ellenőrzésnél csak azt kell megvizsgálnunk, hogy a pozíció zárási ideje (OrderCloseTime() függvénnyel kérdezhető le) nagyobb-e, mint nulla. Amennyiben nem, a pozíció még nyitva van.
  4. A legkomolyabb ellenőrzés a fagyási szint miatt szükséges. Ha nem tudod, hogy mit jelent ez a szó, olvasd el a korábbi cikkemet!

A “freeze level” fogalmat magyarul talán “fagyási szintnek” lehetne nevezni. Amennyiben értéke nagyobb, mint nulla akkor – beállított TP vagy SL esetén – ha az árfolyam ennyi pipen belül tartózkodik a TP vagy SL értéktől, akkor a pozíciót nem lehet lezárni “kézzel”.

Vagyis ha a bróker kondíciói között a FreezeLevel szint nagyobb, mint nulla akkor bizonyos helyzetekben – ha pl. túl közel vagyunk egy függő pozíció élessé válásához, vagy SL/TP teljesüléshez – a piaci (kézi) zárás nem engedélyezett. Ilyenkor hiába próbálkoznánk, a bróker addig biztosan hibaüzenetet fog adni, ameddig az adott zónában tartózkodunk. A fagyási szint ellenőrzése minden pozíciónál más és más, ezen az oldalon a “FreezeLevel Limitation” bekezdés alatt elolvashatod a számítási módszereket. A példakód csak a BUY pozíció freeze level ellenőrzését tartalmazza, ezt ki kell bővíteni a többi pozícióra vonatkozó feltételekkel is.

BUY pozíció esetén a Stoploss kapcsán a Bid – Stoploss különbségének, míg Takeprofit kapcsán a Takeprofit – Bid közti különbségének kell nagyobbnak lennie, mint a freeze level értéke. A freeze level értékét természetesen a MarketInfo() függvénnyel lehet lekérdezni.

  1. A megfelelő zárási ár alatt BUY típusú pozíció esetén a Bid, míg SELL típusú pozíció esetén az Ask árat értem. A függő pozíciók esetén nincs szükség zárási árra, ott simán töröljük a még nem teljesült megbízást. A zárási próbálkozások előtt közvetlenül feltétlen használjuk a RefreshRates() függvényt, amely akkor is frissíti az árakat, ha éppen egy hosszabb zárási folyamatban ragadtunk. E nélkül az előre definiált változók (Bid, Ask, stb.) nem biztos, hogy a legfrissebb adatokat tartalmazzák, és így újabb hibákhoz vezetnek.
  2. Ha idáig eljutottál az olvasásban, akkor egészen kitartó ember vagy! Itt következik a konkrét zárás, amelyet élő pozíciók esetén az OrderClose(), függő pozíciók esetén pedig az OrderDelete() függvénnyel kísérlünk meg. A zárásnak mindenképpen van kimenetele, amelynek mentése kötelező, elhagyása szigorúan tilos! Igényes programozó ilyet nem tesz, ha mégis akkor kizárólag egy – általában valaki más – rémálma közepén.

A visszakapott eredmény egy true / false érték lesz. True érték esetén boldogan megyünk tovább, míg false érték esetén a GetLastError() függvény segítségével le kell kérdeznünk a sikertelenség okát, és a további próbálkozásokat ennek függvényében tesszük majd meg.

  1. True visszatérési érték esetén a példakódban tovább engedjük a ciklust a következő pozíció zárásához, míg false esetén a már ismert hibakód alapján folytatjuk vagy megszakítjuk az adott pozíció zárási próbálkozásait. Megszakításra akkor lehet szükség, ha:
  • nincs már több próbálkozási lehetőségünk. A lehetőségek számát mi határozzuk meg, de érdemes ésszerű keretek között tartani. Az általam használt próbálkozási szám jellemzően 20.
  • valami nagyon komoly, általunk nem javítható hiba történt. Például nincs engedélyezve az élő kereskedés, a piac zárva van, rész-zárás esetén hibás lotméretet adtunk meg, vagy nincs kapcsolat a bróker felé. Ilyen esetekre tökéletesen illik a “Nyomja, mint süket a csengőt” közmondás, ezt elkerülendő inkább szakítsuk meg a while ciklust!

Amennyiben elcsúszott az árfolyam, miközben zárási kérést indítottunk, a következő körben a frissített árral újra kell próbálkoznunk. Ha a trade context (azaz a kérésünket a bróker felé továbbító vonal) foglalt, akkor folyamatosan újra kell próbálkoznunk némi várakozás után. A foglalt állapot nem áll fenn örökké – pláne, hiszen a MT4 újabb változataiban már nem egy, hanem nyolc ilyen vonal áll rendelkezésünkre párhuzamosan. A várakozás azért szükséges, mert senki nem szereti, ha az ajtaján non-stop dörömböl valaki – a brókercég is így van ezzel. A későbbi letiltást jobb elkerülni, ezért érdemes valamennyi (pár száz milliszekundumos vagy több) szünetet beiktatni.

A fentiek alapján a teljes kód:

#include <stderror.mqh>
 
int start() {
 
   int
      m = 0,
      poziciok[],
      elemszam;
 
   ArrayResize(poziciok, 0);
   // a tömb elemeinek nullára állítása
 
   // pozíciók végignyálazása
   for (int k = 0; k < OrdersTotal(); k++) {
 
      // pozíció index alapú kiválasztása
      if (OrderSelect(k, SELECT_BY_POS)) {
 
         // csak azon pozíciók, amelyek azonosak az aktuális instrumentummal
         if (OrderSymbol() == Symbol()) {
 
            // tömb elemei számának rögzítése
            m = ArraySize(poziciok);
 
            // tömb átméretezése eggyel nagyobb elemszámra
            ArrayResize(poziciok, m + 1);
 
            // az új elemszám (teljes méret - 1) feltöltése a tickettel
            poziciok[m] = OrderTicket();
         }
      }
   }
 
   // tömb elemei számának rögzítése
   elemszam = ArraySize(poziciok);
 
   Print ("A tömb elemszáma = ", elemszam);
 
   // végigmegyünk a tömb adatain
   for (k = 0; k < elemszam; k++) {
 
      // zárás a ticket szám alapján
      PozicioZaro(poziciok[k]);
   }
 
   return (0);
}

A PozicioZaro() függvény:

// pozíciózáró függvény
bool PozicioZaro (int ticket) {
 
   bool  
      // alap esetben a függvény végeredményének értéke: hamis
      result = false,
 
      // alap esetben a zárást megpróbáljuk
      closing = true;
 
   // ticket szám ellenőrzése; amennyiben nulla 
   // vagy annál kisebb, nem megyünk tovább
   if (ticket > 0) {
 
      int
         // max. elfogadott csúszás market maker bróker esetén (pontban)
         Slippage = 3,
 
         // esetleges hibakódot tartalmazó változó
         Error,
 
         // hiba esetén várakozás (millisec)
         SleepTime = 300,
 
         // próbálkozások száma
         v = 0
         ;
 
      // pozíció kiválasztása; továbbhaladás, ha sikeres
      if (OrderSelect(ticket, SELECT_BY_TICKET)) {
 
         // while ciklus a hibás próbálkozásokra készülve
         while (v <= SET_POS_CLOSE_RETRY) { // nyitva van még a pozíció? if (OrderCloseTime() == 0) { // hiba változó nullázása Error = 0; // frissítjük a belső adatokat (árak, gyertyák, stb.) RefreshRates(); double // pozíció instrumentuma alapján a freeze level lekérdezése FreezeLevel = MarketInfo(OrderSymbol(), MODE_FREEZELEVEL) * MarketInfo(OrderSymbol(), MODE_POINT), PRICE; // switch szerkezet a pozíció típusához szükséges tevékenységekhez switch (OrderType()) { case OP_BUY: // az instrumentum BID árának lekérdezése PRICE = MarketInfo(OrderSymbol(), MODE_BID); // amennyiben van takeprofit, ellenőrizzük a különbséget if (OrderTakeProfit() > 0 && OrderTakeProfit() - PRICE <= FreezeLevel) { closing = false; } // amennyiben van stoploss, ellenőrizzük a különbséget if (OrderStopLoss() > 0 && PRICE - OrderStopLoss() <= FreezeLevel) { closing = false; } break; case OP_SELL: // az instrumentum ASK árának lekérdezése PRICE = MarketInfo(OrderSymbol(), MODE_ASK); // tennivaló: fagyási szint ellenőrzése break; case OP_BUYLIMIT: // tennivaló: fagyási szint ellenőrzése break; case OP_BUYSTOP: // tennivaló: fagyási szint ellenőrzése break; case OP_SELLSTOP: // tennivaló: fagyási szint ellenőrzése break; case OP_SELLLIMIT: // tennivaló: fagyási szint ellenőrzése break; } // amennyiben nem futottunk bele valami tiltásba, megpróbáljuk a zárást if (closing) { switch (OrderType()) { case OP_BUY: case OP_SELL: if (OrderClose(ticket, OrderLots(), PRICE, Slippage)) { Print (StringConcatenate("#", ticket, " pozíció zárása sikeres!")); result = true; } else { Error = GetLastError(); Print (StringConcatenate("#", ticket, " pozíció zárása sikertelen; hibakód = ", Error)); } break; case OP_BUYLIMIT: case OP_BUYSTOP: case OP_SELLSTOP: case OP_SELLLIMIT: // a függő megbízásokat csak törölni kell if (OrderDelete(ticket)) { Print (StringConcatenate("#", ticket, " pozíció zárása sikeres!")); result = true; } else { Error = GetLastError(); Print (StringConcatenate("#", ticket, " pozíció zárása sikertelen; hibakód = ", Error)); } break; } } else { Print (StringConcatenate("#", ticket, " pozíciót nem zárjuk, mert a fagyási szinthez túl közel van az ár!")); break; } } else { Print ("A #", OrderTicket(), " pozíció már lezárult!"); break; } if (result) { // sikeres zárás esetén megszakítjuk a ciklust break; } // foglalt trade context esetén folytatjuk a ciklust, a próbálkozási szám emelése nélkül if (Error == ERR_TRADE_CONTEXT_BUSY || Error == ERR_SERVER_BUSY || Error == ERR_BROKER_BUSY) { Print (StringConcatenate("A trade context foglalt ekkor: ", TimeToStr(TimeCurrent(), TIME_SECONDS), "; várakozás...")); Sleep (SleepTime); continue; } // Súlyos hibák valamelyike => azonnali megszakítás
            if ((
            Error >= ERR_TRADE_NOT_ALLOWED && Error <= ERR_SHORTS_NOT_ALLOWED) || Error == ERR_INVALID_TRADE_PARAMETERS || 
            Error == ERR_TRADE_EXPIRATION_DENIED || Error == ERR_MARKET_CLOSED || Error == ERR_ACCOUNT_DISABLED || 
            Error == ERR_INVALID_ACCOUNT || Error == ERR_INVALID_TRADE_VOLUME || Error == ERR_NO_CONNECTION ||
            Error == ERR_TRADE_TOO_MANY_ORDERS || Error == ERR_TRADE_PROHIBITED_BY_FIFO || Error == ERR_INVALID_TICKET) {
 
               break; 
            }
 
            v++;
         }
      }
      else {
         Print ("Nem tudom kiválasztani ezt a pozíciót: #", ticket);
      }
   }
 
   // végeredmény visszaadása
   return (result);
}

A forrásfájl letölthető itt:

SET_POS_CLOSE_RETRY konstanssal állítható a próbálkozások száma, mely alapból 15. A fagyási szint vizsgálatát csak a BUY típusú pozíciókra csináltam meg, házi feladat a többi pozíciótípusra elkészíteni – a korábban említett linken lévő táblázat segít a nem túl bonyolult képlet kapcsán. Az első switch szerkezettel a különböző típusú pozíciók különböző fagyási szint számításait lehet elválasztani – ha ilyen akadály nem gördül elénk, akkor a closing változó true kiindulási értékét megtartva haladhatunk tovább a konkrét zárás felé, mely szintén egy switch szerkezeten belül helyezkedik el. A BUY és SELL pozíciók, illetve a függő megbízások egy-egy csoportot képeznek: az előbbiek az OrderClose(), míg az utóbbiak az OrderDelete() függvény segítségével zárulhatnak. Amennyiben a művelet sikeres, a result változó értékét true-ra állítjuk, és ezen keresztül az iteráció végén kilépünk a while ciklusból. Amennyiben valami balul sül el, a result változó kiindulási false értéke sehol nem változik meg true-ra, így maximum SET_POS_CLOSE_RETRY alkalommal a while cikluson belül ragadunk, amely újra és újra le fog futni. Amennyiben súlyos hiba miatt hiúsul meg a zárás, azonnal megszakítjuk a while ciklust, míg ha a trade context volt foglalt, várunk SleepTime -nyi milliszekundumot, aztán folytatjuk a while ciklust anélkül, hogy a próbálkozások számát (v változó) növelnénk.

Zárszó

Amennyiben kérdés, vagy javaslat merülne fel benned a fentiekkel kapcsolatban, szólj hozzá!

Írta: | 2017-08-20T18:59:54+00:00 2013. augusztus 15., csütörtök|MQL4, Olvasnivaló programozóknak|11 hozzászólás

A szerzőről:

Radulovic Attila vagyok, a radu.hu tulajdonosa és szerkesztője. Remélem, hasznosnak találod az oldalamon található anyagokat! Célom, hogy hatékony segítséget nyújtsak Neked a kereskedéssel és az automatizálással kapcsolatban. Érdekel a véleményed, kérlek írd meg kommentben!

11 hozzászólás

  1. dr.herrik 2013. augusztus. 20. kedd - 13:53- Válasz

    Kedves Radu!

    Köszönöm hogy ilyen gyorsan reagáltál a levelemre,és hogy nem csak velem,
    de minden kedves olvasóval megosztottad a „vesztő pozíciók zárását”!
    Így leírva és látva lépésről lépésre,nagyon érthető és már nem is olyan bonyolult!.:)
    Idáig az 1. módszerrel próbálkoztam,(közben rájöttem mi volt a hiba),de a tőmbe írásos módszer volt amit kerestem!
    Működik,és igazából most nincs is kérdésem, csak szeretném még egyszer megköszönni,
    hogy ilyen önzetlenül segítesz másoknak!
    Tök jó az oldal,mindenkinek ajánlom,
    Jövök neked eggyel….

    H.

    • Radulovic Attila 2013. augusztus. 20. kedd - 14:48

      Helló! Örülök, hogy tetszik a site és hogy segíthettem! 😉 Lájkolj, ossz meg, az bőven fedi a köszönet kategóriát:)

  2. tomolt 2017. március. 1. szerda - 09:42- Válasz

    Szia Radu,

    Ha egy pozíciót lefelezek mondjuk, azaz OrderClose-al zárom az eredeti lot felét, akkor az OrdersTotal a megmaradt pozíciót nem találja meg. Szerinted miért?

    • Radulovic Attila 2017. március. 2. csütörtök - 07:29

      Azért, mert a lezárult pozíciók a Számlatörténet fülre kerülnek, amelyre a MODE_HISTORY -val tudsz hivatkozni a MODE_TRADES helyett.

      A maradvány (= még élő) pozíció megjegyzésében látni fogsz egy from #x szöveget. A # utáni rész a rész-zárt pozíció ticket száma, őt megtalálod majd a számlatörténetben. Az ő megjegyzésében pedig a maradványpozíció ticket számát találod meg, to #y szöveggel.

  3. tomolt 2017. március. 2. csütörtök - 09:58- Válasz

    Az addig rendben van, hogy a lezárt részpozíció a számlatörténetbe kerül és MODE_HISTORY-val érem el. A kérdés az, hogy a megmaradt (még élő) részpozíciót miért nem látom a MODE_TRADE-el? (bocsi, lehet nem fogalmaztam egyértelműen)

    • Radulovic Attila 2017. március. 2. csütörtök - 11:34

      Látod, csak más ticket szám alatt. Ennek a ticket számnak a megkeresését írtam le fentebb.

  4. tomolt 2017. március. 2. csütörtök - 14:14- Válasz

    OrderSelect (i, SELECT_BY_POS)-al megyek végig az összes orderen és eggyel kevesebbet talál, mint a pozi felezése előtt. Azért este még ellenőrzöm…

  5. tomolt 2017. március. 2. csütörtök - 23:08- Válasz

    Rájöttem a hibára: a pozi felezésekor a megmaradt pozinak a commentjét megváltoztatja a kliens az eredetiről. Ez okozta a problémát. Mivel az OrderSelect blokkomban a comment az egyik feltétel, ami beazonosítja az ordereket, így a megváltozott commentű ordert nem vette számításba.
    Köszi a segítséget!

    • Radulovic Attila 2017. március. 4. szombat - 19:42

      Igen, valóban, a rész-zárás sajnos likvidálja a pozíció megjegyzését. Ebbe a problémába már én is belefutottam 😉

      Szuper, hogy rájöttél!

      Amennyiben a rész-zárás elvárás az érintett stratégiában, az ember kénytelen más adattárolási módszert választani (pl. globális változók).

  6. fenlaci 2017. június. 21. szerda - 15:07- Válasz

    Szia Radu!

    Hogyan lehet két dimenziós tömböt átméretezni, ha mondjuk az egyik dimenzió a poziciók száma , a második fixen mindig 8 elemű?
    Pl:
    adott pozíciókhoz tartozó adatokat szeretném kezelni
    m=OrderTotal()
    aa[m, 1]= ticket
    aa[m,2]=date
    aa[m,3]=price…
    Köszi a segítséget!

    • Radulovic Attila 2017. június. 24. szombat - 19:59

      Szia!

      Simán használhatod az ArrayResize() eljárást.

      ArrayResize(aa, 3);

      Ez csak az első dimenziót fogja 3 eleműre módosítani, a második vagy harmadik dimenzióhoz nem nyúl.

Hagyj üzenetet