csütörtök, február 24, 2011

Építkezés - folytatás...

Tegnap elkezdtük a FOREX robotunk építését, ma pedig folytatjuk.
Beszéltünk tegnap a stratégiáról egész részletesen, sőt, el is kezdtük a kódolást a felhasználótól bekért kezdő adatokkal és az első lépést is megtettük a robot építésében.

Ma egy újabb lényeges résszel folytatjuk, mégpedig a start() függvényünket csiszolgatjuk még tovább.
Egész konkrétan azt fogjuk lekódolni, amikor a program megvizsgálja van-e nyitott pozíciónk.
Nem is nagyon szaporítom a szót fölöslegesen, íme a kód, aztán pedig megbeszéljük :
   nyitott = false;
   orders = OrdersTotal();
  
   for(int pos=0;pos
   {
      if(OrderSelect(pos,SELECT_BY_POS)==false)
         continue;
      else
            if (OrderSymbol() == Symbol())
               nyitott=true;
   }
Gondolom emlékeztek rá, hogy tegnap felvettünk egy globális változót, amely bool típusú. Ha ez igaz (azaz true) értéket tartalmaz, akkor van nyitott pozíciónk, ha pedig hamis (false), akkor nincs.

Felmerülhet a kérdés, hogy mi a fenének ez rögtön az elejére. A magyarázat rém egyszerű. Azért, mert ha nem ezzel kezdenénk a műveleteket, akkor a programunk minden alkalommal, amikor pozíció nyitási jeleket lát a chart-on, minden alkalommal nyitna egy pozíciót. Minden egyes gyertyánál, amíg csak jel van.
Ez oda vezetne, hogyha mondjuk egy 100 gyertyából álló trend indulna el, akkor nyitna 100 pozíciót. Ha megfelelően nagy pozíció méretet állítunk be, akkor egyszer csak nem tudnánk már nyitni, mert a teljes tőkénket befektettük a sok pozíció nyitással és gyakorlatilag elfogyna a pénzünk. A baj nem itt van, hanem ott, amikor lezárjuk őket. Ugyanis a java része veszteséges lenne, és csak kis hányada - egész konkrétan a legelőször nyitottak - lennének nyereségesek.

Ez azt jelenti, hogy viszonylag gyorsan el is fogyna a pénzünk...

Itt azért meg kell jegyeznem a teljesség kedvéért, hogy vannak olyan technikák, amikor nyitunk egy pozíciót, majd amikor jónak látjuk, nyitunk egy másikat is ugyanabba az irányba, azután még egyet, de ezek száma korlátos és egyszerre zárjuk le őket. de ezeket jól átgondoltan akkor nyitjuk, amikor megbizonyosodtunk arról, hogy a trend merre megy és amikor mindet egyszerre bezárjuk, akkor legfeljebb az utolsó veszteséges, a többi viszont óriási nyereséggel zárul. Ezt hívják piramis építési technikának, beszélünk is majd róla később, de most egyenlőre maradunk az egy nyitott pozíciónál.

Nézzük a kódot! Mivel minden új gyertya "keletkezésekor" lefut a robot lelke, vagyis a start() függvényünk, ezért minden új gyertya indulásakor meg is vizsgáljuk van-e nyitott pozíciónk.
Ehhez legelőször is azt feltételezzük, hogy nincs nyitott pozíciónk és beállítjuk hamis értékre a nyitott nevű változónkat.
Ezután használjuk az orders változót, amely egész típusú és egy egyszerű függvénnyel megadjuk neki azt, hogy hány nyitott pozíciónk van. Erre való az OrdersTotal() beépített MetaTrader függvény.
Ez a függvény visszaad egy egész számot, amely a nyitott pozíciók méretének számát tartalmazza.

Ezzel itt vége is, gondolhatnánk, hiszen ez a függvény megmondja mennyi nyitott pozíciónk van, minek a további kód? Aki ezt gondolja, teljesen igaza is van. Egyetlen egy dolgot azonban nem szabad elfelejtenünk!
A MetaTrader több devizapáron is megjeleníti a chart-ot és mindegyiken köthetünk üzletet, viszont ha pozíció nyitási jelet látunk mondjuk az EURUSD-n, de az USDCHF-en már van nyitott pozíciónk, akkor EURUSD-n nem fog nyitni, mert az OrdersTotal() függvény beleszámolja az ÖSSZES nyitott pozíciót!

Ezért hát a kód többi része. Egy egyszerű for ciklust indítunk el, amely csak akkor fut le, ha a változó értéke nagyobb nullánál, tehát ha legalább egy nyitott pozíciónk van.
Az OrderSelect() függvény lépeget végig a nyitott pozíciókon, méghozzá a paramétereiben megadott módon.
Kétféle módon tudja ezt megtenni, vagy a pozíció nyitási jegy száma alapján (erről mindjárt beszélünk kicsit), vagy pedig pozíció szám alapján, amely pozíció nem más, mint a pozíció nyitásának sorszáma időrendben, függetlenül az instrumentumtól, vagy bármi mástól.
Mivel a nyitási jegy számát nem tudjuk, ezért pozíciónként megyünk végig a nyitott üzleteken.

A pozíció nyitási jegyekről pár szóban.
Annak idején, amikor még a kereskedést a tőzsdén a placcon nyomták, akkor a brókerek egy cetlire írták fel, hogy miből mennyit akarnak vásárolni, vagy eladni. Aztán amikor jött az ajánlati könyv vezetője, beordított egy árat, hogy akkor most venne ennyiért ezt és ezt. A brókerek meg üvöltve tették meg az ajánlataikat, amelyet, ha elfogadott az ajánlati könyvbe a kereskedő, akkor az volt a hivatalos tranzakció, új alapokra helyezve a következő tranzakciót (nő, vagy csökken az ár mindegyik után, hiszen ha sok az eladó, akkor az lenyomja az árat, ha pedig a vevő sok, akkor az meg felveri az árat).
Ezeknek a cetliknek, mint kötési jegyeknek, egyedi sorszáma van, melyet az ajánlati könyvbe bejegyeznek.

A dolog az intelligens számítógépek korában is pontosan így működik, azzal a különbséggel, hogy nem brókerek üvöltenek a placcon, hanem számítógépek zúdítják a biteket a brókercéghez, ahol az ajánlati könyvet kezelik. Vannak olyan bróker cégek, ahol az ajánlati könyv legfelső néhány tételét látni is lehet a weboldalon, így lehet tudni nagyjából, hogy eladói, vagy vevői nyomás van a piacon.

Mivel a MetaTrader nem csak devizapárok, hanem részvények (pontosabban származtatott részvény termékek, EFT-k) kereskedésére is alkalmas, ezért a deviza kereskedelemben is megtartották a szisztémát és ugyanígy kezelik az ajánlatokat.

Tehát ott tartottunk, hogy a nyitott pozíciónak van egy egyedi azonosító száma, amely megkülönbözteti az összes többitől, legyen az bármelyik chart-on.
Az OrderSelect() függvénynek két paramétere van. Az egyik a pozíció száma, vagy pedig a kötési jegy száma, a második paramétere pedig azt mondja meg, hogy az első paraméter egy kötési jegy szám, vagy sorszám.
Ezen adatok alapján megkeresi a pozíciót és kiválasztja (mintha csak egy listában sárga kijelölő filctollal kisárgítaná nekünk). Ezek után az összes pozícióra vonatkozó függvény ezzel az egy pozícióval foglalkozik.
Van jó pár, akit érdekel és otthon van alapszinten az angol nyelvben, végig nézegetheti a Help-ben. Csak párat megemlítek közülük, le lehet kérdezni a kötés idejét, méretét, az árat, az aktuális profit/loss-t, a stop-ot hová húztuk, a take profit hol van, stb.
De megtudhatjuk belőle azt is, hogy melyik instrumentumra kötöttük azt a bizonyos pozíciót.

Éppen ez kell nekünk. Tehát a for ciklusunk végig megy a nyitott pozíciókon sorszám szerint, és megnézegeti, hogy a pozíció milyen instrumentumon köttetett. Az OrderSymbol() függvény visszaadja a nyitott pozíció instrumentumát, a Symbol() függvény pedig azt mondja meg, hogy az aktuális chart éppen milyen instrumentumon van.
Az OrderSelect() függvény visszatérő értékként azt adja vissza, hogy sikeres volt-e a pozíció kiválasztása. Tehát a kód egyszerűen végig sétál a nyitott pozíciókon, ha van egyáltalán, és megnézi, hogy bármelyik nyitott pozíció instrumentuma megegyezik-e a chart-on lévővel.

FOREX robotunk újabb alkatrésze jön, mégpedig a pozíció nyitásokhoz szükséges vizsgálat.
Itt is gyorsan mutatom a kódot, azután megbeszéljük :
if (iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,0,1) != EMPTY_VALUE && iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,1,1) == EMPTY_VALUE) // Long
      {
         TMTrend="Long";
      }
Ez itt egy egyszerű vizsgálat, if utasítással. A trükk azonban a részletekben rejlik.
Gyors magyarázat az egyszerűség kedvéért. Az iCustom függvénnyel lehet az egyéni indikátorokat lekérdezni. Az összes beépített indikátornak van saját beépített függvénye (pl. a MACD-nek az iMACD függvény).
Az indikátorok lekérdezését végző függvények paraméter listája mindig két érték megadásával kezdődnek.
Az első a megfelelő instrumentum megadása, a második pedig az időtáv, amin dolgoznia kell.

Ha egészen precízek szeretnénk lenni, akkor az instrumentumnak megadhatnánk a Symbol() függvényt, amiről fentebb már volt szó, az időtávnak pedig a Period() függvényt. Ez az aktuális chart éppen használatos időtávját adja meg.

Ehelyett azonban az alapértékeket adjuk meg, az instrumentumnak a NULL értéket, az időtávnak pedig a 0-t.
Ha megnézzük a help-ben a iCustom() függvényt, akkor látjuk, hogy az instrumentum paraméterként megadott NULL érték az aktuális instrumentumot jelenti, a 0 időtáv pedig szintén az aktuálisat.

Persze megadhatunk fix értékeket is, de akkor csak azon az instrumentumon és csak azon az időtávon fog működni a FOREX robotunk.
Folytatva az iCustom() függvény magyarázatát, a következő paraméter az egyéni indikátor neve string-ként megadva. Ez a név pontosan az kell, hogy legyen, amit az egyéni indikátorok listában látunk a Navigation ablakban. Különben nem fogja megtalálni. A mi esetünkben ez a TrendMagic egyéni indikátor lesz.
Ezután következnek a TrendMagic paraméterlistája, majd két fontos információ.
Az első azt adja meg, hogy a TrendMagic indikátor melyik paraméterét kérdezzük le, a második pedig azt, hogy a pillanatnyi gyertyához képest visszafelé hányadik gyertyánál lévő értékre vagyunk kíváncsiak.

Az egyéni indikátorok lekérdezhető paraméterei függenek az egyéni indikátortól. Egy mozgóátlagnak (iMA) egyetlen paramétere van, de egy ADX-nek rögtön három. A paraméter számozás 0-val kezdődik, tehát a legelső paraméter a 0-ás, a második az 1-es és így tovább.

Honnan lehet tudni, hogy melyik a 0-ás paraméter? Onnan, hogyha feltesszük a chart-ra, fentről lefelé ez jelenik meg legelőször, általában az indikátor neve mellett.

A mi esetünkben a TrendMagic-nek két paramétere van, jól lehet egyetlen vonalat látunk a képernyőn, ami hol piros, hol kék. A kék és piros értékeket külön kezeli, ezért ez két különféle paraméter.
A 0-ás érték az a kék vonal esetén él, az 1-es érték pedig a piros vonal esetén él. Tehát ha a 0-ás paraméter nem üres, akkor tudjuk, hogy a TrendMagic vonala kék, azaz emelkedő trendet mutat, ha az 1-es paramétere nem üres, akkor piros a vonal és csökkenő trendet mutat.

Az iCustom() függvény legutolsó paramétere, amit már megbeszéltünk, az pedig a pillanatnyi gyertyát megelőző gyertya sorszámára utal, az ott lévő indikátor értéket vizsgáljuk. Itt a pillanatnyi gyertya előtti, tehát a legutolsó bezárt gyertyánál lévő indikátor értékre vagyunk kíváncsiak, mivel a pillanatnyi gyertya mindig változik, tehát a hozzá tartozó indikátor érték is. Egy folyamatosan változó értékre alapozni egy pozíció nyitást, öngyilkosság, de minimum hazárdjáték. A tőzsdézés viszont nem hazárdjáték.

Az említett if utasítás tehát egész egyszerűen azt az esetet vizsgálja, hogy a TrendMagic paramétere nem üres, de az 1-es paramétere üres (vagyis a vonal kék ÉS nem piros), akkor a TrendMagic emelkedő trendet mutat, vagyis long pozíció nyitására van lehetőség eszerint az indikátor szerint.

Ugyanez fordítva a short trend nyitásának feltétele :
if (iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,0,1) == EMPTY_VALUE && iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,1,1) != EMPTY_VALUE) // Short
      {
         TMTrend="Short";
      }
Tehát itt pedig az előző feltétel ellentétét vizsgáljuk.
Felhívnám a figyelmet az egyenlőség és a nem egyenlőség vizsgálatát végző operátorokra. A dupla '=' jel a megfelelőséget, a '!=' pedig a nem megfelelőséget vizsgálja. Azért említem meg, mert tipikus programozói hiba szokott lenni, hogy a dupla '=' jel helyett csak szimplát használnak, ami ugye értékadást jelent. Tehát a feltétel MINDIG igaz lesz, de ha egy változó van a bal oldalon, akkor az felveszi azt az értéket, ami a jobb oldalon van. Ez nem egészséges.

A másik tipikus hiba, hogyha valaki más nyelvben is programozik, mondjuk például Visual Basic-ben, akkor a nem megfelelőség jeleként a kacsacsőrt használja, vagyis a '<>' jelet. Ez azonnali fordítási hibát dob, tehát ezt nem nehéz kivédeni, de sok kezdő nem érti a hiba üzenetet, hogy miért nem tetszik a fordítónak ez a megszokott jel... Hát mert csak. Ez egy C-szerű nyelv, márpedig ennek a nyelvnek a szintaktikája nem szereti a más nyelvben megszokott jelet. Ennyi az egész.

Tehát túl is vagyunk a TrendMagic indikátorunk által mutatott jelek értelmezésén, amit el is tettünk egy string változóba. Azért string-be, mert ez így mindenkinek olvashatóbb, jobban is lehet értelmezni.

Következzék hát a TVI jeleinek vizsgálata.
Mivel ez pont ugyanúgy működik, mint a TrendMagic így nincs meglepetés, csak a paraméterek száma különbözik, mert itt három paraméter van, nem kettő. De ettől eltekintve ugyanígy kell vizsgálni a zöld és piros értékeket. Sőt, mi több, még a paraméterek lekérdezése is ugyanígy működik :
if (iCustom(NULL,0,"TVI_2color",TVI_rs,TVI_rs,5,TVI_TF,0,1) != EMPTY_VALUE && iCustom(NULL,0,"TVI_2color",TVI_rs,TVI_rs,5,TVI_TF,1,1) == EMPTY_VALUE)
      {
         TVITrend="Long";
      }
     
      if (iCustom(NULL,0,"TVI_2color",TVI_rs,TVI_rs,5,TVI_TF,0,1) == EMPTY_VALUE && iCustom(NULL,0,"TVI_2color",TVI_rs,TVI_rs,5,TVI_TF,1,1) != EMPTY_VALUE)
      {
         TVITrend="Short";
      }
Tehát szinte ugyanazt a kódot kapjuk, csak egy paraméterrel több van és másképp hívják az indikátort (és persze más paramétert kapnak az indikátorok is).

Ezzel szinte készen is vagyunk, már ami az indikátorok vizsgálatát illeti, a következő post-ban már a pozíció nyitásokkal leszünk elfoglalva, azután pedig stop-loss húzásokkal.

Aztán, ha ezekkel mind meg vagyunk, akkor jöhet a puding próbája, a back test.

Jó elmélkedést, programozást, help olvasgatást!

Nincsenek megjegyzések:

Megjegyzés küldése