péntek, február 25, 2011

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

Szép jó estét mindenkinek! Gondolom már vártátok a folytatást, hát itt van.

Eddig ugye szó esett a FOREX robot és a MetaTrader kapcsolatáról, szó esett a robot felépítéséről is.
Sőt, mi több, el is kezdtük építeni a kis okos robotunkat MetaTrader-ben, a saját C# programozási nyelvén.

Eddig ott tartunk, hogy az indikátorok alapján eldöntöttük, hogy merre az arra, vagyis azt, hogy a mi kis okos FOREX robotunk long - azaz vételi -, vagy short - azaz eladási - pozíciót nyisson.
Most azt fogjuk megbeszélni, hogy na, akkor hogyan is nyissunk pozíciót!

Csapjunk is a közepébe, íme a kód és már meg is beszéljük hogyan működik :
      if (TMTrend == "Long" && TVITrend == "Long" && !nyitott)
      {
         ticket=OrderSend(Symbol(),OP_BUY,maxlots,Ask,slippage,0,0,"TrendMagic-TVI - Long",MathRand(),0,Green);
         err = GetLastError();
         if (err != 0 && ticket < 0)
         {
            Print("Error # ", err, "  ", ErrorDescription(err));
         }
      } 
Első ránézésre talán bonyolultnak tűnik, de nem az.
Minden ahhoz az alapvető feltételhez van kötve, hogy az indikátorok mit mutatnak.
Tehát három feltétel meglétét vizsgáljuk. Az első kettő a két indikátor együttállása, a harmadik pedig a start() függvény elején vizsgált feltétel szerint beállított nyitott nevű változónk. Ez ugye igaz értéket vesz fel, ha az aktuális devizapáron van már nyitott pozíciónk.
Tehát a feltétel vizsgálja, hogy long nyitási lehetőség van-e és azt, hogy NINCS nyitott pozíciónk. Mert ha már van, akkor nem nyitunk rá. (ez majd a rákötésnél lesz érdekes, de ott még nem tartunk)
Tehát a '!' jel az, amely a tagadás (szakmaibban szólva negálás) ebben a nyelvben, ha egy változó hamis értéket tartalmaz, akkor ez igazzá változtatja és fordítva. Tehát, ha van nyitott pozíciónk, akkor ezzel a jellel hamis értéket kapunk és nem teljesül az if utasításunk, ha pedig nincs nyitott pozíciónk, akkor igaz értéket kapunk és ha mindkét indikátor long-ot mutat, akkor már folytatjuk is a végrehajtást a következő utasítás soron.

Nem gatyázunk, ez már maga a pozíció nyitás. Sajnos ez is kétesélyes, vagy sikerül, vagy nem. Éppen ezért ha nem sikerül, akkor arról tudnunk kell.
De ne szaladjunk ennyire előre.
Az OrderSend() függvény gondoskodik arról, hogy a pozíció megnyíljon, bármilyen irányba. Nyilván az első paramétere az instrumentum neve - vagyis a devizapár neve - lesz, ahol a pozíciót nyitni szeretnénk, a második a már sokkal érdekesebb, mert itt mondjuk meg miféle pozíciót nyitunk.
Nézzük csak, miket lehet nyitni :
  • OP_BUY : ez egyszerűen egy long, azaz vásárlási (buy) pozíció nyitást jelent, semmi extra
  • OP_SELL : a long párja, vagyis a short, eladási (sell) pozíció
  • OP_BUYLIMIT : na, ez már érdekes, ha az ár elér egy bizonyos szintet, akkor nyit egy long-ot
  • OP_SELLLIMIT : ez az előző short testvére
  • OP_BUYSTOP : ez nagyon hasonló a buy limit-hez, de kicsit másképp működik, mindjárt kitérünk rá
  • OP_SELLLIMIT : ez pedig az előző párja
Mi a különbség a LIMIT és a STOP pozíció nyitás között? Sok!
A LIMIT mindig egy felső határt, a STOP pedig egy alsó határt jelöl. Vagyis, például egy buy limit esetén amikor a limitet meghaladja az ár, akkor nyit pozíciót, minimálisan a megadott limit áron és nem kevesebbért.
A buy stop pedig akkor fog pozíciót nyitni, amikor az ár leesik arra a bizonyos árszintre, amit megadunk és legfeljebb azon az áron nyit pozíciót, amit megadunk. Tehát a limit ár általában magasabb, mint a pillanatnyi ár, a stop pedig alacsonyabb, de ez a stratégiától függ természetesen. Mindenesetre így tervezték ezeket a pozíció nyitási lehetőségeket.

Két következtetést lehet levonni ebből. Az egyik, hogy a limit egy felső határ fölött, de legalább annyiért, ha a beérkező tick magasabb, akkor magasabban fog pozíciót nyitni, a stop pedig egy alsó határ alatt nyit pozíciót, maximum annyiért, de ha a tick alacsonyabb, akkor alacsonyabban fog pozíciót nyitni.
A másik következtetés, hogy ezek nem azonnali pozíció nyitások, hanem feltételesek, tehát amíg a pozíció meg nem nyílik, törölni is lehet őket mindenféle következmények nélkül.

Az első kettőt fogjuk használni, ugyanis ezek az azonnali pozíció nyitások. A többit nem is fogjuk használni, ezek általában kézi kötésnél játszanak szerepet, automatikus kereskedésben még sosem láttam ilyen kötést...

Nos, visszatérve a pozíció nyitó OrderSend() függvényünkre, a harmadik paraméterünk azt mondja meg, hogy mekkora méretű pozíciót nyitunk. Erről a nap végére tartogatok egy kis magyarázatot.
A következő paraméter az ár, amin szeretnénk pozíciót nyitni.Ezt megadhatjuk közvetlenül is, de ennek nem sok értelme van, ugyanis a program végrehajtása során ez az ár változhat. Tehát érdemes a pillanatnyi áron vásárolni.

No és akkor itt most álljunk meg egy szóra, mert itt jön az a rész, amit néha a kereskedők elfelejtenek. Aki nem felejti el ezeket, nyugodtan átlépheti ezt a bekezdést, de szerintem érdemes elolvasni. Fél perc...
A devizapárokkal történő kereskedés két áron zajlik. A vételi és az eladási áron. A MetaTrader a vételi árat Ask-nak hívja, az eladási árat pedig Bid-nek. A kettő közötti árrést nevezzük spread-nek.
Mivel a brókereknek az eladási ár a legfontosabb, így az eladási árat követhetjük nyomon a grafikonokon. Tehát venni az Ask áron tudunk, eladni viszont a Bid áron.

Éppen azért, ha pillanatnyi áron szeretnénk long pozíciót nyitni, akkor az az Ask ár lesz.

A következő három paraméter a slippage, amit már a múltkori post-ban megbeszéltünk, majd a stop-loss és a take-profit következik, melyeket hogyha 0-ra állítunk, akkor nem létezők lesznek, most ezt használjuk.
A következő paraméter egy egyszerű szöveges komment, amit úgy fogalmazunk meg, ahogy akarunk.

Az ezt követő paraméter a magic number, amit senki sem tud pontosan mi célt szolgál, ami biztos, hogy a felhasználó saját döntése alapján azt ír ide, amit akar, mint egyedi azonosítót.
Egyetlen dolgot fedeztem fel ezzel kapcsolatban, hogy a MetaTrader nem szereti, ha két különböző devizapáron azonos magic number-rel jelzett pozíció van nyitva. Egyszerűen megkavarodik és hülyeséget csinál.
Éppen ezért ide mindig kivétel nélkül valamilyen véletlen számot szoktam tenni. Lehet egy jól meghatározott konstans érték is, de akkor mindenképpen gondoskodjunk arról, hogy a különböző devizapárokon különböző magic number-eket használjunk!
Ezt követően a pozíció nyitás lejárati idejét lehet megadni, ami persze csak akkor értelmes, ha limit, vagy stop pozíciókat nyitunk és akkor is csak addig, amíg meg nem nyílik a pozíció. Ezért ez nekünk mindig 0.

Megnyitott pozíciót csak lezárni lehet, törölni nem!
Ezt azért húztam alá, mert nagyon fontos! Törölni csakis azokat a megbízásokat lehet, amelyek még nem teljesültek, amelyek már teljesültek, azok nyitott pozíciók és csak lezárni lehet őket, ez persze már pénzbe kerül (ára a spread-től és a pozíció méretétől függ), míg a nem teljesült megbízásokat büntetlenül törölhetjük bármikor, ingyenesen.

A legutolsó paraméter pedig a chart-on a nyitást ábrázoló nyilacska színe, ami szintén szabadon választott.

A függvény visszatérési értéke a pozíció nyitási jegy száma, ha sikerült a nyitás. (emlékszünk ugye a múltkori leírásra, miért kezeli így a program ezeket).
Ha ticket nem valami értelmes értéket tartalmaz, akkor nem sikerült a pozíció nyitás és akkor a hibát a felhasználó tudtára kell adni. Erre valók a következő sorok.

Először is pozíció nyitás után kiolvassuk a hiba számát a GetLastError() függvénnyel. Ha ez nem nulla és a ticket változónk is valamilyen nullánál kisebb értéket tartalmaz (tipikusan -1), akkor nem sikerült a pozíció nyitás és a hibár egy Print() függvénnyel a Naplóba írjuk.
A Napló tartalma a MetaTrader kereskedési felületén megtekinthető bármikor a program futásakor, tehát ide mindig üzenhetünk, hogyha a felhasználót szeretnénk tájékoztatni valamiről, ami nem a chart-on kell, hogy legyen.

Tehát ennyit a long pozíció nyitásáról.
A short pozíció nyitása is ugyanilyen egyszerű, csak ott a Bid árat kell megadnunk, és már nyílik is a pozíció.
Tehát így néz ki a short nyitás :
if (TMTrend == "Short" && TVITrend == "Short" && !nyitott)
      {
         ticket=OrderSend(Symbol(),OP_SELL,maxlots,Bid,slippage,0,0,"TrendMagic-TVI - Short",MathRand(),0,Green);
         err = GetLastError();
         if (err != 0 && ticket < 0)
         {
            Print("Error # ", err, "  ", ErrorDescription(err));
         }
      }

Látható milyen egyszerű.

A következő post-ban a stop húzást fogjuk megvalósítani, ezáltal lesz egy stratégiánk, ami jelek alapján pozíciót nyit és zár, indulhat a back test. A back test-ek végrehajtásához a legjobb a hétvége, akkor lehet nyugodtan tesztelgetni.
Tehát igyekszünk holnap minden finomra csiszolni a programunkon, hogy vasárnap békésen tesztelhessen mindenki!

A pozíció méretről ígértem egy kis elmélkedést.
A FOREX esetében - amikor is nem részvényeket vásárlunk, hanem devizapárokat - nem darabonként vesszük, adjuk, hanem kötegekben, csomagokban. Ezt a FOREX úgy nevezi, hogy Lot. Egy ilyen kereskedési egység 100.000 alapdevizát jelent, ha azt a tizedespont után 5 számjegy követi.
Ehhez a tőkeáttétel szerinti fedezet szükséges, ha a tőkeáttételünk 1:100, akkor ennek az egy század részét kell a számlán tartanunk, ha vásárolni akarunk. Ez 1000 USD. Tehát ez nagyon sok. Éppen ezért, mivel a pénzünknek csak maximum 3%-át kockáztatjuk, a maximális kötési mennyiséget be kell lőnünk.

Ha 0.01 lot-ot kötünk, akkor "csak" 10 USD-t kockáztatunk, tehát az óvatosság nem csakhogy nem árt, hanem inkább még használ is.

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!

szerda, február 23, 2011

Építkezés

Már megint jó sok idő telt el, mentségemre legyen mondva, elég rendesen felborult a megszokott életritmusom. De lassan elülnek a hullámok, a FOREX persze marad, az automatikus kereskedelem és a robot építés még mindig a szívem csücske, így természetesen nem hagyok fel ezzel. Minden nap új tapasztalatokat szerzek.

Fura a mai cím, de mégis jó ide, ugyanis elkezdjük "megépíteni" a robotunkat. Nem kell igazi robotra gondolni, de ebben a szakmában így hívják azokat a programokat, amelyek automatikusan kereskednek.

FIGYELEM! Az itt közzétett programkódok saját szellemi termékeim, de szabadon felhasználhatja bárki. Felelősséget azonban nem tudok érte vállalni,  mint ahogyan a kód használatából származó anyagi károkért sem! MINDENKI SAJÁT FELELŐSSÉGÉRE HASZNÁLJA! A kódok CSAK oktatási céllal kerültek ide! Kellő hozzáértés és fegyelem nélkül TILOS éles számlán használni!

A FOREX robotunk két egyéni indikátort használ, mindkettőt kiveséztem a legutóbbi post-ban. Az egyik a TVI, a másik pedig a TrendMagic. Illetve van még egy harmadik indikátor is, amely a stop-loss-okat húzogatja utánunk. Ő a Parabolic SAR.

A két egyéni indikátorral az a szerencsénk, hogy mindkettő forráskódját fel lehet lelni az interneten, ha esetleg valaki nem találná, vagy nem biztos abban, hogy azt találta meg, amire szükség van, az bátran írjon nekem levelet, "postafordultával" elküldöm az indikátorok forráskódját. Javaslom, hogy módosítás nélkül használja mindenki!

Mindkét indikátor színekkel jelzi, hogy szerinte a trend merre megy. De sajnos egyedül egyik sem megbízható, ezért van szükség arra, hogy legalább kettőt használjunk.

A TVI-ből ráadásul kettőt fogunk használni, mert egy kevés lesz.
Kicsit furmányos ez az indikátor, mert nem egy, hanem két forráskódból áll. Az egyik a TVI.mq4, amely a normális TVI jeleket számolja. A másik a TVI_2color.mq4, amely a nevéhez méltóan két színnel jelöli a trend lehetséges irányát. Ez utóbbi indikátor képtelen egyedül működni, csakis kizárólag az előzőekben említett TVI.mq4 indikátorral együtt működik, tehát mindkettőt be kell másolni az indicators mappába.

A TVI-nek három, a TVI_2color-nak négy paramétere van. Ezek magyarázatára nem térnék ki, legyen elég annyi, hogy elmondom hogyan kell beállítani őket.

A TVI paraméter beállításai tehát a következők :
  • r : 50
  • s : 50
  • u : 5
  • tf : 0
Ezekkel a paraméterekkel fel is tehetjük egy chart-ra tanulmányozni, hogy mit is mutat! Szerintem tanulságos.

A TrendMagic-ről is írtam részletesen legutóbb, összesen két paramétere van :
  • CCI : 50
  • ATR : 12
Ezt is feltehetjük ugyanarra a chart-ra, amire feltettük a TVI-t.
Más dolgunk már nincs is, mint megbarátkozni a Parabolic SAR-ral. Ennek is egy színes változatát használjuk, csak azért, mert programozás szempontjából könnyebb ezt kezelni. Aki átírta, úgy nevezte el, hogy "Parabolic trendchaser", vagyis trend hajhász... :o)
Beállításai :
  • AlertsEnabled : false
  • Step : 0.06
  • Maximum : 0.1
  • Precision : 7
  • TimeFrame : 0
A paraméterek értelmezését most szintén nem taglalnám! A lényege, hogy így kell beállítani. Aki nem akar a Parabolic trendchaser-rel bajlódni, az használhatja a beépített Parabolic SAR-t is, a Step és a Maximum paramétereket felhasználva ugyanezt az eredményt fogjuk kapni, csak nem lesz szép színes és később a programozás során nehezebb kezelni.
Ha ezekkel a beállításokkal ezt is felteszitek a chart-ra, akkor látható, hogy igen szűk stop-okat lehet vele húzni, ha a bróker hagyja. De erről kicsit később.

Tehát, meg is vagyunk az indikátorok beállításával. Látszik szépen a chart-on, hogy milyen jól együtt járnak ezek az indikátorok :

TVI-TrandMagic stratégia


A stratégia pedig rém egyszerű, amikor a TVI zöld, a TrendMagic kék, és a Parabolic alsó kék pöttyöket rajzol, akkor long pozíciót nyitunk és a Parabolic-nak megfelelően húzzuk a stop-ot. Amikor a trend vált, akkor egyszerűen "kistoppolódunk" és már készen is állunk a következő pozíció nyitásra.

A programunk tehát a következő részekből fog állni :
  • Indikátorok adatainak begyüjtése
  • A jelzések alapján pozíció nyitás
  • A nyitott pozíciók stop-jainak húzogatása
A zárással azért nem foglalkozunk, mert a kistoppolódás miatt úgysem kell zárnunk.

A program kód eleje mindig azzal kezdődik, hogy a felhasználótól bekérjük az indulási paramétereket, illetve az alapbeállítások módosítását tesszük lehetővé számukra. Akinek nem tetszik az 50-es érték a TVI esetében, bátran átállíthatja saját felelősségére.

Nem szeretném elmagyarázni a nyelv egyes elemeit, remélem mindenki programozott már és tudja mi az a változó, meg hogy globális változó, függvényhívás, deklaráció, definíció, stb.

Tehát a program azoknak a változóknak a definíciójával kezdődik, amelyeket a felhasználó számára is elérhetővé szeretnénk tenni, egyben a programunk ezeken keresztül tartja a kapcsolatot a felhasználóval, ha adatokat kérünk be.
Ezek a változók a hagyományos C#-ban megszokott típusúak lehetnek, de az elé írt extern klauzulával lehet őket láthatóvá tenni a felhasználó számára.
A mi esetünkben a következő lesz a lista :
extern double maxlots=0.01;
extern int slippage=5;
extern int TrendMagicCCI=50;
extern int TrendMagicATR=5;
extern int TVI_rs=50;
Látható tehát, milyen adatokat kérünk, illetve az is látható, hogy nem csak kérünk, de alapbeállításként fel is ajánlunk egy-egy értéket. Ennek ugyebár ott az értelme, hogyha a felhasználó ránk bízná a program paraméterezését, akkor is legyen valami induló adat.

Nézzük sorban őket:
  • maxlots : ez a kötéseink során fontos, azt fogja tartalmazni, milyen méretű pozíciót nyithatunk. Ez most egyenlőre egy fix érték lesz, amit ide beállítunk.
  • slippage : ez egy csúszó érték, amely szintén a kötések során fontos. Azt az értéket tartalmazza, amivel a bróker eltérhet az általunk megadott kötési, zárási ártól. Erről mindjárt írok egy-két sort, mert ez fontos, meg kell értenetek, mire való ez.
  • TrendMagicCCI : ez ugyebár a TrendMagic indikátorunk CCI paramétere
  • TrendMagicATR : ez pedig a TrendMagic indikátor ATR paramétere
  • TVI_rs : ez pedig a TVI indikátorunk r és s értéke (a kettőt mindig ugyanarra az értékre állítjuk) az u értéket viszont nem engedjük ki a kezünk közül, az fixen 5.
Tehát pár mondat a slippage-ről. Amikor a MetaTrader-en keresztük utasítást adunk a brókernek, hogy nyisson, vagy zárjon egy pozíciót, akkor meg kell adnunk egy árat. Ez az ár azonban nem biztos, hogy a pillanatnyi ár. Ha ugyanis miközben az utasítás az internet éterén keresztül beérkezik a brókerhez, ott feldolgozásra kerül és közben egy újabb tick érkezik, az ár máris megváltozik és a kötést nem engedi végrehajtani a rendszer.
Ezért vezették be a slippage nevű paramétert, amely megadja, hogy a bróker a pozícióink kezelésekor mennyivel térhet le az utasításban megadott ártól. Ez minden esetben az aktuális valutapár, vagy részvény értékének felel meg, tehát ha a tizedespont után 5 számjegyet látunk, akkor az ötödik számjegyre vetített értéket jelenti. Az alapként beállított 5-ös érték EURUSD esetében (ahol 5 számjegy követi a tizedespontot, de a negyedik számjegy jelenti az 1 PIP-et) mindössze 0.5 PIP csúszást enged meg a brókernek.

Ha ezt az értéket 0-ra állítanánk, egy gyorsan változó piac esetében többszöri próbálkozás ellenére sem biztos, hogy ott nyitunk pozíciót, ahol akarunk. Esetleg előfordulhat az is, hogy egyáltalán nem tudunk pozíciót nyitni, vagy zárni.

Az 5-ös érték általában jól működik és kellően kicsi ahhoz, hogy kötni lehessen. Nem ajánlott módosítani, de ezt mindenki maga döntheti el. Ha kivesszük az extern klauzulát előle, a felhasználó nem tudja piszkálni, de az érték ott lesz benne.

Ahogyan már egy korábbi post-ban ecseteltük, az Expert Advisor három részből épül fel. Van egy init(), egy deinint() és egy start() függvénye. Ezek közül az init() akkor fut le, amikor a chart-ra feltesszük a programot, de csak egyetlen egyszer fut le. A feladata azoknak a tevékenységeknek az elvégzése, amelyeket a kereskedés előtt egyszer meg kell tenni.
Ugyanígy a deinit() függvény pedig akkor fut le, amikor levesszük a chart-ról a FOREX robotunkat, ez is csak egyszer fut le, hasonlóképpen az előzőekhez a futás utáni takarítás a dolga.
A start() függvény viszont a szíve az Expert Advisor-nak, ugyanis ez fut le minden egyes alkalommal, amikor csak érkezik egy tick, azaz egy kereskedési adat.

Miután programunk nagyon egyszerű és nem helyez el mindenféle objektumot a chart-on (erről is lesz később szó), ezért csak a start() függvény megírására kell koncentrálnunk.

Mielőtt azonban a lelkét kezdenénk fejlesztgetni a robotunknak, érdemes az extern változók után felvenni a globális változóinkat.
Azokat a változókat érdemes ide felvenni, amelyek értékére két beérkező tick között is szükségük lesz, tehát ha ezt a start() függvényen belül vennénk fel, minden egyes tick alkalmával, amikor lefut  start() függvény, a változó újra és újra létrejönne, de azzal a kezdőértékkel, amivel definiáljuk, illetve ha az nincs, akkor valamilyen véletlen szerű kezdőértékkel.
Ilyenek azok a globális változók, amelyek a program futása közben végig meg kell, hogy őrizzék az értéküket, amíg le nem állítjuk.

Tipikusan állapot jelző változókra van itt szükség, vagy számlálókra, és ehhez hasonlókra.
Valami ilyesmi lehet :
int currentbar;
string TMTrend, TVITrend;
int orders;
bool nyitott;
 Némi magyarázat :
  • A currentbar változó fogja tartalmazni az aktuális gyertyák számát, amelyet a chart megjelenít. Ez fontos lesz majd.
  • a string változók arra valók, hogy az egyes indikátorok által jelzett trendet jelöljék. Ez lesz az alapja a pozíció nyitásnak
  • Az orders változó arra lesz hivatott, hogy a pozíció kezelések alkalmával a pozíció egyedi azonosítóját tartalmazza majd.
  • A nyitott nevű változónk pedig azt mutatja majd, hogy van-e nyitott pozíciónk már, avagy sem
 Most pedig lássuk a FOREX robot lelkét!

Azzal fogjuk kezdeni, hogy amikor egy újabb tick érkezik, megvizsgáljuk, hogy ez a tick egy új gyertya legelső tick-je, vagy pedig egy már folyamatban lévő gyertya valahányadik tick-je.
Miért jó ez nekünk?
Azért, mert a tőzsdeiskolákban mindenhol azt tanítják, hogy csak a már bezárult gyertya az igazi gyertya, a még be nem zárult gyertya változhat, hiszen a piacon bármi megtörténhet. Előfordulhat, hogy a bika gyertyából az adott időintervallumon belül hirtelen medve gyertya lesz. Na, ekkor aztán fuccs a stratégiának. Tehát csak a bezárult gyertyákkal foglalkozunk.

A kód így néz ki :
if (currentbar < Bars)
      currentbar = Bars;
   else
      return;
A magyarázat pedig nagyon egyszerű.
A currentbar nevű változónk, amelyet globálisnak vettünk fel, tartalmazza a chart-on található gyertyák számát. Kezdetben egyébként valami teljesen kiszámíthatatlan értéket tartalmaz, tehát elsőre biztosan lefut a start() függvényünk. (ha ez semmiképpen sem akarjuk, akkor az init() függvényünkben ennek a változónak adhatunk értéket, valahogy így : currentbar = Bars; Így pontosan azt az értéket fogja tartalmazni, ami a chart-on van, így a programunk addig nem fut le, amíg az aktuális gyertya le nem zárul. Íme egy lehetséges és értelmes módja az init() függvény használatának.)
A Bars rendszerváltozó az aktuális gyertyák számát tartalmazza, amelybe beletartozik az éppen be nem zárt gyertya is. Ez a globális rendszerváltozó a MetaTrader saját globális változója és csakis olvasható értéket tartalmaz. Lesz még pár ilyen amivel megismerkedünk.
Tehát a kódrészlet úgy működik, hogy ha az általunk megjegyzett gyertyák száma kevesebb, mint a chart-on lévő aktuális gyertyák száma, akkor bizony ez éppen egy új gyertya nyitását jelenti, tehát jól megjegyezzük mennyi is a chart-on a gyertyák száma és hagyjuk lefutni a start() függvényt.
Ha viszont az eltárolt gyertyaszám megegyezik a chart-on lévő gyertyaszámmal, akkor az érkező tick az aktuális gyertyához tartozik, tehát egy return utasítással be is fejezzük a start() függvényünk futását.

Miután az új gyertyát megkezdő tick az előző gyertya záró tick-je is egyben, így a start() függvényünk minden esetben lefut, amikor egy újabb gyertya lezárul, de csakis akkor, nem máskor. Ilyen egyszerű.

A legközelebbi post-ban innen folytatjuk a kód írását. Talán kedvcsinálónak ennyi is elég lesz mára. Igyekszem nem három hónap múlva folytatni... :o)