péntek, május 20, 2011

A robotunk egyben, az utolsó csavarig

Ismét jó sok idő telt el, volt, aki kommentben sürgetett meg.
Nos, tehát a teljes kódot közzé teszem, hiszen ez az értelme a blognak.

Azonban szeretnék leszögezni ismételten valamit, méghozzá nyomatékosan!
Ez az Expert Advisor nem alkalmas éles kereskedésre, kizárólag oktatási céllal készült. Aki éles számlán üzemelteti, a veszteségeiért eme blog írója semmilyen felelőséget sem válallal! A kód egésze, illetve egyes elemei szabadon felhasználhatóak, kivéve a kereskedelmi célú felhasználást! Kereskedelmi célra felhasználni a szerző beleegyezése nélkül tilos!

Remélem mindenki számára érthető, ugyanis nem szeretnék senkivel sem vitatkozni, különösen nem jogi kereteken belül! Tehát ez itt egy oktató program! Semmi más! A célja az egyes programozási fogások, ötletek bemutatása.

A FOREX robotunk megírása koránt sem fejeződött be, most jön ugyanis a tesztelése, egész konkrétan a backtest, ami egy jó támpont arra, hogy mennyire jó az ötlet maga. Illetve fogjuk látni a hibákat is, no meg azt is, hogy mikor köt rosszul. Sokat számít, hogy jól ki tudjuk értékelni a backtest-et.

Ha megtörtént a backtest, akkor jöhet egy tisztességes Money Management is hozzá, végül pedig egy demo számlán fogjuk tesztelni, és a teszt eredményét fogjuk kiértékelni. Hozzátenném, hogy egy egyébként elvben jól működő, de mégis veszteséget termelő programből nyereségeset lehet varázsolni egy kitűnő Money Management-tel...Ehhez persze az kell, hogy az elv működjön, vagyis lehetőleg több nyerő kötése legyen, mint vesztő és lehetőleg ez ne a véletlenen múljon, vagyis jókor kell pozíciót nyitnia. A jó pozíció nyitás azt jelenti, hogy a várható nyereség nagyobb, mint a veszteség (haladóknak : RR nagyobb, mint 1!).

Tehát minden egyéb magyarázat nélkül, következzék a robot kódja. Annyit tennék még hozzá, hogy aki esetleg nem érti minden egyes részét, bátran olvasson vissza a korábbi bejegyzéskbe, ugyanis az utolsó sorig megmagyaráztam a robot kódját.
A kód tehát :

//+------------------------------------------------------------------+
//| TrendMagic-TVI.mq4 |
//| Copyright © 2011, János Antal |
//| http://tozsdecapak.blogspot.com |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2011, János Antal"
#property link "http://tozsdecapak.blogspot.com"
#property show_inputs
#include <stderror.mqh>
#include <stdlib.mqh>

extern double maxlots=0.01;
extern int slippage=5;
extern int TrendMagicCCI=50;
extern int TrendMagicATR=5;
extern int TVI_rs=50;
extern int TVI_TF=0;


int currentbar;
string TMTrend, TVITrend;
int orders;
bool nyitott;

int init()
{
}
int deinit()
{
}
int start()
{
int err, ticket;
double stoploss, ParUp, ParDn;


// Csak akkor fusson le, ha a gyertya már befejeződött

if (currentbar < Bars)
currentbar = Bars;
else
return;

nyitott = false;
orders = OrdersTotal();

for(int pos=0;pos<orders;pos++)
{
if(OrderSelect(pos,SELECT_BY_POS)==false)
continue;
else
if (OrderSymbol() == Symbol())
nyitott=true;
}


// TrendMagic alapján nyitunk pozíciót

if (iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,0,1) != EMPTY_VALUE && iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,1,1) == EMPTY_VALUE) // Long
{
TMTrend="Long";
}

if (iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,0,1) == EMPTY_VALUE && iCustom(NULL,0,"TrendMagic",TrendMagicCCI,TrendMagicATR,1,1) != EMPTY_VALUE) // Short
{
TMTrend="Short";
}

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";
}

//Pozíció nyitások

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));
}
}

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));
}
}

//Stop-loss húzás
orders=OrdersTotal();

if (orders != 0)
{
for(pos=0;pos<orders;pos++)
{
if(OrderSelect(pos, SELECT_BY_POS,MODE_TRADES))
{
ParUp=iCustom(NULL,0,"Parabolic trendchaser", false, 0.06,0.1,7,0,0,1);
ParDn=iCustom(NULL,0,"Parabolic trendchaser", false, 0.06,0.1,7,0,1,1);

if (OrderType() == OP_BUY && OrderSymbol() == Symbol() && ParDn != 0)
{
stoploss = ParDn;

if (OrderModify(OrderTicket(),OrderOpenPrice(), stoploss, OrderTakeProfit(),0,Blue))
Print("Stop-loss módosítva!");
else
{
Print("Stop-loss nem módosult! ", DoubleToStr(stoploss,5));
Print("Hibaüzenet : ",ErrorDescription(GetLastError()));
}
}else if (OrderType() == OP_SELL && OrderSymbol() == Symbol() && ParUp != 0)
{
stoploss = ParUp;

if (OrderModify(OrderTicket(),OrderOpenPrice(), stoploss, OrderTakeProfit(),0,Blue))
Print("Stop-loss módosítva!");
else
{
Print("Stop-loss nem módosult! ", DoubleToStr(stoploss,5));
Print("Hibaüzenet : ",ErrorDescription(GetLastError()));
}
}
}else
Print("Nem sikerült húzni a Stop-Loss-t, nem találom a pozíciót!");
}
}
}


A kódot a MetaEditor szerkesztő mezőjébe kell bemásolni, de előtte egy "üres" Expert Advisor-t kell készíteni. Ez a következőképpen megy, ha már a MetaEditor-ban vagyunk :
  • File menü
  • New menüpont
  • A megjelenő ablakban az Expert Advisor-t kell bejelölni
  • A tovább gomb után pedig olyan nevet adunk neki, amilyet akarunk, de célszerű a forráskód fejlécében lévő nevet adni neki (TrendMagic-TVI).
  • Célszerű az ablak tartalmátteljesen kitörölni és bemásolni a fenti kódot.
Ha ezzel meg vagyunk,akkor már csak annyi a dolgunk, hogy lefordítjuk a kódot, amit vagy az F5 billentyűvel tehetünk meg, vagy pedig a menü alatti ikon sorban található Compile gombbal.
A fordítás után a chart-ra visszatérve a navigátor ablakban az Expert Advisor mappában találjuk azon a néven, amilyen nevet adtunk neki létrehozáskor.

Aki kedvet érez hozzá, nyugodtan kísérletezzen vele, de szigorúan csak demo számlán!!!
Ez a program még nem elég érett az éles számlán történő használatra, de folyamatosan érlelni fogjuk. Most még elég nyers ez a kód.

Aki esetleg fordítási hibával találkozik (előfordulhat, hogy a HTML-be illesztés során homokszem kerül a gépezetbe), nyugodtan írja meg nekem, bár leteszteltem, és le kellene fordulnia simán.
Egyébként elég beszéldessek a hibaüzenetek.

Kellemes babrálást!

szerda, március 23, 2011

Készre csiszolás

Megint kicsit elmaradtam, de majd most megpróbálom bepótolni a várva várt készre csiszolással...

Tehát FOREX robotunk már készen van nagyjából, bár igazából még csak a pozíció nyitásokat beszéltük meg. Arról volt szó, hogy pozíció zárás nem is lesz benne, hiszen stop-loss-t fogunk beállítani, ez fogja zárni a pozíciónkat, méghozzá úgy, hogy az árhoz minél közelebb húzzuk.
A folyamatos ár után húzott - követő - stopunkat addig húzogatjuk, amíg a stop-loss a kötési ár fölé/alá (függően attól, hogy long, vagy short pozíciónk van) kerül, lehetőleg minél jobban.
Ekkor, ha az ár visszaesik, akkor a stop-loss ugyan bezárja a pozíciót, de ekkor már nyereséges lesz.
Na, jó, de hogyan lehet ezt csinálni?

A dolog rém egyszerű, a Parabolic trendchaser egyéni indikátort fogjuk ügyesen felparaméterezni, és az ő jelzéseire állítjuk be a stop-loss-t. Sajnos ez nem lesz mindig lehetséges, de erről majd picit később.

Mindenek előtt, a programunk elején, közvetlenül a start() függvény elejére illesszünk be egy új sort, ahol három új változót fogunk felvenni.
Tehát a start() függvényünknek így kell kezdődnie :
int start()
  {
   int err, ticket;
   double stoploss, ParUp, ParDn;
A három double változó feladat lesz a stop-loss kiszámítása és húzása. Az első két int változó feladata a pozíció nyitások lebonyolítása, a ticket számát tartalmazza és az esetleges hibakódot.

No, de nézzük csak hogyan kell a stopot húzni.A teljes kód a következőképpen néz ki, a jelentését mindjárt meg is beszéljük :
orders=OrdersTotal();
      
if (orders != 0)
      {
         for(pos=0;pos
         {         
            if(OrderSelect(pos, SELECT_BY_POS,MODE_TRADES))
            {  
               ParUp=iCustom(NULL,0,"Parabolic trendchaser", false, 0.06,0.1,7,0,0,1);
               ParDn=iCustom(NULL,0,"Parabolic trendchaser", false, 0.06,0.1,7,0,1,1);
               
               if (OrderType() == OP_BUY && OrderSymbol() == Symbol() && ParDn != 0)
               {
                  stoploss = ParDn;                  
                  
                  if (OrderModify(OrderTicket(),OrderOpenPrice(), stoploss, OrderTakeProfit(),0,Blue))
                     Print("Stop-loss módosítva!");
                  else
                  {
                     Print("Stop-loss nem módosult! ", DoubleToStr(stoploss,5));
                     Print("Hibaüzenet : ",ErrorDescription(GetLastError()));
                  }
               }else if (OrderType() == OP_SELL && OrderSymbol() == Symbol() && ParUp != 0)
               {
                  stoploss = ParUp;

                  if (OrderModify(OrderTicket(),OrderOpenPrice(), stoploss, OrderTakeProfit(),0,Blue))
                     Print("Stop-loss módosítva!");
                  else
                  {
                     Print("Stop-loss nem módosult! ", DoubleToStr(stoploss,5));
                     Print("Hibaüzenet : ",ErrorDescription(GetLastError()));
                  }
               }                  
            }else
               Print("Nem sikerült húzni a Stop-Loss-t, nem találom a pozíciót!");
         }
      }
Az OrdersTotal() függvény elárulja nekünk, hogy mennyi nyitott pozíció van ezen a számlán, minden devizapáron.
A legelső if utasítás arról gondoskodik, hogy csakis kizárólag akkor foglalkozzunk a stop húzással, ha van nyitott pozíciónk.
Ezután egyesével végig szaladunk egy for ciklus segítségével a pozíciókon. A ParUp és a ParDn értékek kiszámításával kezdjük, hiszen nemsokára szükségünk lesz rá. Ezek adják majd a stop szinteket.

A következő if utasítással azt vizsgáljuk, hogy :
  • Long pozíció-e az éppen vizsgált soron következő pozíció
  • Az aktuális chart-nak megfelelő devizapáron köttetett-e (képzeljük csak el, ha egy EURUSD-n futó programunk lezárná az EURHUF long pozíciónkat... Kellemetlen lenne...)
  • És azt is vizsgáljuk, hogy van-e kiszámított érték egyáltalán, mert ha nincs, akkor minek túráztatjuk magunkat
Ha a feltételek teljesülnek, akkor az OrderModify() függvénnyel beállítjuk a stopot. A top értéke a Parabolic trendchaser indikátor alsó pöttyének az értéke lesz, ezért a stoploss változónkba is ezt írjuk be és ezt is állítjuk be stopnak.
Figyeljük meg, hogy az aktuális adatokat kikérjük a pozícióról és azokat vissza is írjuk az OrderModify() függvénybe, tehát csak a stopot módosítjuk. (tehát az OrderTicket() függvény megmondja az aktuálisan kiválasztott pozíciónk ticket számát, az OrderOpenPrice() elárulja milyen áron nyitottuk a pozíciót, ha van take profit beállítva, akkor az OrderTakeProfit() ezt is elárulja. Mindezeket visszaírjuk a módosításba eredeti formájukban, tehát nem bántjuk őket, így tudjuk csak  stop-loss-t módosítani)

Természetesen vizsgáljuk a visszatérési értékét a függvénynek, tehát ha igaz értékkel tér vissza, akkor közöljük a program felhasználójával, hogy sikerült, ha pedig nem sikerült, azt is közöljük, de a hiba okát is kiírjuk, erre való az ErrorDescription(GetLastError()) összefüggés.
Az igazsághoz persze az is hozzátartozik, hogy a programunk elejére be kell szúrnunk még két sort, ahhoz, hogy a hibakiírás működhessen, mégpedig ezt a két sort :
#include <stderror.mqh>
#include <stdlib.mqh>
Ezekkel biztosíthatjuk, hogy az előbb említett hibakezelő függvények működhessenek.
Érdemes a többi include utasítás alá beilleszteni, ha van. De mindenképpen a többi #-kal kezdődő sorokhoz.

A  short pozíciónál ugyanez az eljárás, de ott a Parabolic trendchaser indikátor felső pöttyének az értékét fogjuk beállítani.

A FOREX robotunk még nem az igazi, összeraktuk, készre csiszoltuk, de még nem políroztuk fel, ugyanis vannak még szépség hibái.
Például az, hogyha a Parabolic trendchaser indikátor éppen nem mutat alul pöttyöt, csak felül (mert ugye ez is egy Parabolic SAR rokon és vagy fönt, vagy lent vannak pöttyei), akkor nem húz stopot a program.
Akkor sem fog stopot húzni, hogyha túlságosan közel vagyunk pillanatnyi árhoz.

Ezeket az eseményeket le kell kezelnünk, különben stop nélkül maradunk és nem, vagy túl későn lesz lezárva a pozíciónk, amiből komoly veszteségek származhatnak.
Mindenesetre ez a kis program jól szemlélteti, hogy hogyan érdemes nekifeszülni egy ilyen program megírásának és mire érdemes gondolni közben.

A következő post-ban a legutóbbi két esemény kezelésével foglalkozunk, megoldást keresünk rá, szemléltetve a MetaTrader további lehetőségeit.

Ezután a teljes programkódot közzéteszem, hogyha valakinek nincs kedve az egészet begépelni, vagy ha esetleg lemaradt, akkor egyben láthatja a kódot.
Telis-tele lesz megjegyzésekkel, hogy mi miért van, így talán azok is követni tudnak majd, akik eddig nem olvasták el az összes post-ot.

Ha pedig mindenki a kezébe kaparintotta a működő robotot, jöhet a tesztelés, melynek fázisait szintén végignézzük, illetve a fejlesztés további szakaszai jönnek, a veszteségek kiküszöbölése és egy jó kis money management kerül bele.

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)