Po vytvoření knihovny s pomocnými funkcemi se můžeme pustit do samotného operačního jádra strategie. Naše strategie bude využívat hodnot indikátorů Moving Avarage (MA) a William’s %R (WPR). Předem upozorňuji, že následující kód nelze považovat za strategii, která je určena pro reálné obchodování, je určena pouze jako výukový příklad pro zpracování vstupních pokynů na základě analýzy, kterou si trader stanoví. Veškeré rizika spojená s případnými finančními ztrátami na základě této strategie nese čtenář sám. Pro testování strategie a všeobecně jakýchkoli strategií je třeba použít buď demo účet, nebo tester strategií.
1. Scénář
Naše strategie, jak již bylo řečeno, využije indikátory MA a WPR. Použijeme dva MA indikátory a jeden WPR s následujícími hodnotami:
První MA linie (zelené):
- - Období: 5
- - Metoda: Exponenciální
- - Cena použita na: Close
Druhá MA linie (červené):
- - Období: 7
- - Metoda: Exponenciální
- - Cena použita na: Open
WPR:
- - Období: 40
Uvažujeme, že začneme nakupovat, pokud dojde ke křížení zelené MA a červené MA směrem nahoru. K tomu musí platit, že WPR musí křížit spodní hranici, kterou si stanovíme na -80.
Pro zahájení prodeje platí opačný postup. Zelená MA musí křížit červenou MA směrem dolů a WPR musí křížit horní hranici, která je -22.
Uzavírání pokynů chceme provádět na základ otevíracích signálů. Pokud nakupujeme a je signál k prodeji, zavřeme pokyn a přejdeme do prodeje, a naopak.
Samozřejmostí bude podpora pro stanovení výšky ceny pro StopLoss.
2. Založení souboru
V MetaEditoru klikněte na tlačítko Nový nebo z menu File -> New založte nový soubor. V průvodci vyberte položku Expert Advisor a klikněte na tlačítko Další. V dialogovém okně zadejte do pole Name název skriptu MyEA (případně si jej pojmenujte dle své potřeby). Pole Parameters zatím necháme prázdné, protože si vstupní parametry vytvoříme v kódu. Klikněte na tlačítko Dokončit. Průvodce vygeneruje skript, ve kterém jsou funkce init(), deinit() a start(). Rušivé komentáře můžete případně smazat.
3. Připojení souboru TradeLib
Nyní si připojíme do skriptu námi vytvořenou knihovnu funkcí. Na začátek kódu pod definice #property link a před funkci init() vložte následující kód:
// připojení knihovny s funkcemi pro obchodování
#include <TradeLib.mqh>
Příkazem #include můžete vložit jakýkoli jiný skript. Pokud chcete vložit více souborů, napíšete #include s názvem souboru na další řádek. Název souboru může být uzavřen do znaků <> nebo do uvozovek. Pokud je uzavřen ve znacích <>, MetaTrader se dívá do složky experts\include nebo do složky experts. Pokud je název uzavřen v uvozovkách, hledá se soubor v cestě, odkud se spouští skript, který jej připojuje.
4. Vstupní parametry pro uživatele
Vstupní parametry slouží pro uživatele, který má možnost upravit vlastnosti chování strategie. Záleží na vývojáři strategie, co vše dovolí nastavit. Parametry jsme mohli zadat přes průvodce, ale stejně tak je můžeme přidat ručně v kódu v průběhu vývoje dle potřeby. Pod kód, kde jsme připojili knihovnu TradeLib, napiště následující seznam vstupních parametrů:
// počet lotů, které se mají obchodovat
extern double Lots = 0.1;
// počet pipů pro nastavení SL
extern int StopLossPips = 30;
// počet pipů, po kterých se má nastavovat SL
extern int TrailStopLossPips = 15;
// počet pipů pro nastavení TP
extern int TakeProfitPips = 0;
// určuje, zda se má obchodovat nepřetržitě
extern bool Trade24H = true;
// hodina pro zahájení obchodu
extern int TradeHourStart = 6;
// minuta pro zahájení obchodu
extern int TradeMinuteStart = 30;
// hodina pro ukončení obchodu
extern int TradeHourStop = 22;
// minuta pro ukončení obchodu
extern int TradeMinuteStop = 30;
Jejich význam nebudu rozepisovat, ponechám vysvětlení v komentářích, které je vždy nad daným parametrem. Pouze uvedu, že klíčovým slovem extern říkáme, že tato vlastnost je vstupní (neboli externí) parametr, který může uživatel měnit. Každý parametr přednastavíme vhodnou hodnotou.
5. Pomocné „globální“ proměnné
Pod vstupní parametry přidáme svoje pomocné proměnné, které budeme používat v některých funkcích našeho kódu. Pod vstupní parametry přidejte následující kód:
// měnovým pár, který se obchoduje
string symbol;
// ID otevřeného pokynu
int ticketId = -1;
// Určuje typ operace pokynu (True = BUY, jinak SELL)
bool isBuy;
// spodní hranice WPR
double wprLimitLow = -80;
// horní hranice WPR
double wprLimitHigh = -20;
6. Úprava funkce init()
Jedinou úpravu, kterou uděláme ve funkci init(), je nastavení proměnné symbol. Tím zajistíme, že se po dobu běhu strategie nebude měnit měnový pár. Funkce init() by měla nyní vypadat takto:
// inicializace
int init()
{
symbol = Symbol();
return(0);
}
7. Funkce pro získání hodnot indikátorů
Jelikož naše strategie se opírá o hodnoty indikátorů a předpokládáme, že hodnoty budeme potřebovat získat opakovaně, pomůžeme si pomocnými funkcemi. Na konec skriptu vložte následující funkce:
// vrátí hodnotu MA pro zelenou linii
double getGreenMA(int shift)
{
return(iMA(symbol, Period(), 5, 0, MODE_EMA, PRICE_CLOSE, shift));
}
// vrátí hodnotu MA pro červenou linii
double getRedMA(int shift)
{
return(iMA(symbol, Period(), 7, 0, MODE_EMA, PRICE_OPEN, shift));
}
// vrátí hodnotu William's %R
double getWPR(int shift)
{
return(iWPR(symbol, Period(), 40, shift));
}
Funkce getGreenMA() vrací hodnotu pro MA zelené linie. Funkce getRedMA() vrací hodnotu pro MA červené linie. Funkce getWPR() vrací hodnotu pro WPR. Všechny tři funkce přebírají parametr, ve kterém se určuje index svíčky, jejíž hodnotu chceme získat.
8. Funkce pro zjištění křížení MA
Další dvě funkce nám zjistí, zda dochází ke křížení linií obou MA, podle kterého hlídáme signály pro vstup. Na konec skriptu vložte následující kód:
// pomocná funkce, která zjistí, zda dochází ke křížení linie A směrem nahoru přes linii B
// hodnoty jsou dány v parametrech
// hodnoty musí být pro stejné časové pásmo (A1 = B1 v čase, A2 = B2 v čase)
bool isCrossingUp(double valueA1, double valueA2, double valueB1, double valueB2)
{
if(valueA1 <= valueA2 || valueA1 < valueB1 || valueA2 > valueB2)
return(false);
else
return(true);
}
// pomocná funkce, která zjistí, zda dochází ke křížení linie A směrem dolu přes linii B
// hodnoty jsou dány v parametrech
// hodnoty musí být pro stejné časové pásmo (A1 = B1 v čase, A2 = B2 v čase)
bool isCrossingDown(double valueA1, double valueA2, double valueB1, double valueB2)
{
if(valueA1 >= valueA2 || valueA1 > valueB1 || valueA2 < valueB2)
return(false);
else
return(true);
}
9. Funkce zjišťující signál pro otevření a zavření pozice
Nyní když máme funkce, které nám zjistí hodnoty indikátorů a zda dochází ke křížení MA či nikoli, můžeme napsat funkce, které zjišťují, zda je signál pro otevření pozice BUY nebo SELL a pro zavření daného pokynu. Na konec skriptu vložte následující kód:
// zjistí, zda je signál pro otevření BUY pozice
bool isOpenBuySignal()
{
int shift = 1; // posuneme se o jednu svíčku zpět
double maG1 = getGreenMA(shift); // hodnota zelené MA
double maG2 = getGreenMA(shift + 1); // hodnota zelené MA o jednu starší
double maR1 = getRedMA(shift); // hodnota červené MA
double maR2 = getRedMA(shift + 1); // hodnota červené MA o jednu starší
double wpr1 = getWPR(shift); // hodnota WPR
double wpr2 = getWPR(shift + 1); // hodnota WPR o jednu starší
double wpr3 = getWPR(shift + 2); // hodnota WPR o dvě starší
// zjistíme, zda je křížení MA směrem nahoru
// a zda WPR směřuje přes spodní hranici směrem nahoru
if(isCrossingUp(maG1, maG2, maR1, maR2) &&
(wpr1 > wprLimitLow && (wpr2 < wprLimitLow || wpr3 < wprLimitLow)))
{
return(true);
}
else
{
return(false);
}
}
// zjistí, zda je signál pro zavření BUY pozice
bool isCloseBuySignal()
{
return(isOpenSellSignal());
}
// zjistí, zda je signál pro otevření SELL pozice
bool isOpenSellSignal()
{
int shift = 1; // posuneme se o jednu svíčku zpět
double maG1 = getGreenMA(shift); // hodnota zelené MA
double maG2 = getGreenMA(shift + 1); // hodnota zelené MA o jednu starší
double maR1 = getRedMA(shift); // hodnota červené MA
double maR2 = getRedMA(shift + 1); // hodnota červené MA o jednu starší
double wpr1 = getWPR(shift); // hodnota WPR
double wpr2 = getWPR(shift + 1); // hodnota WPR o jednu starší
double wpr3 = getWPR(shift + 2);
// zjistíme, zda je křížení MA směrem dolu
// a zda WPR směřuje přes horní hranici směrem dolu
if(isCrossingDown(maG1, maG2, maR1, maR2) &&
(wpr1 < wprLimitHigh && (wpr2 > wprLimitHigh || wpr3 > wprLimitHigh)))
{
return(true);
}
else
{
return(false);
}
}
// zjistí, zda je signál pro zavření SELL pozice
bool isCloseSellSignal()
{
return(isOpenBuySignal());
}
Funkce dle scénáře vyhodnocují signál pro otevření a zavření pozice. U zavírání je to jednoduché, protože se odkazujeme na otevření pozice pro opačný směr. Otevírací funkce si zjišťují potřebné hodnoty indikátorů, provedou dle analýzy výpočet křížení MA pro daný směr a jako upřednostňující informaci porovnávají křížení WPR s hranicí limitu, kterou jsme si stanovili v globálních proměnných.
10.Kontrolní funkce
Nyní si napíšeme pomocné kontrolní (neboli operační) funkce, které nám budou hlídat, zda je čas obchodovat a zda je čas otevřít či uzavřít pokyn. Na konec skriptu vložte následující kód:
// zjistí, zda se může provádět obchodování
bool canTrade()
{
// kontrola, zda neběžíme v testovacím režimu a zda jsou zaplé strategie
if(IsTesting() == false)
{
if(IsExpertEnabled() == false)
{
Alert("Pokud chcete, aby EA bylo aktivní je třeba zapnout strategie");
return(false);
}
}
if(Trade24H)
return(true);
else
return(IsTimeToTrade(TradeHourStart, TradeMinuteStart, TradeHourStop, TradeMinuteStop));
}
// provede kontrolu otevíracích signálů
void checkOpenSignals()
{
if(canTrade() == false)
return;
if(isOpenBuySignal()) // kontrola na BUY signal
isBuy = true;
else if(isOpenSellSignal()) // kontrola na SELL signal
isBuy = false;
else
return;
ticketId = OpenTicket(symbol, isBuy, Lots, StopLossPips, TakeProfitPips);
}
// provede kontrolu zavíracích signálů
void checkCloseSignals()
{
if(canTrade() == false)
return;
bool doClose = false;
if(isBuy)
doClose = isCloseBuySignal();
else
doClose = isCloseSellSignal();
// pokud je čas zavřít pokyn, uzavřeme jej
if(doClose)
{
CloseTicket(ticketId);
ticketId = -1;
}
}
První funkce canTrade() nám zjistí, zda můžeme otevírat či zavírat pokyny na základě vstupních parametrů uživatele. Také kontroluje, zda máme zapnuté strategie. Druhá funkce checkOpenSignals() zkontroluje, zda lze otevřít pokyn a pokud ano, tak zkontroluje, zda je signál pro nákup nebo pro prodej. Pokud máme signál, funkce otevře pokyn. Třetí funkce checkCloseSignals() kontroluje, zda je signál pro zavření pokynu. Pokud ano, provede zavření.
11.Úprava funkce start()
Jelikož máme napsané všechny potřebné funkce, můžeme napsat tělo funkce start() a řídit tak tok obchodování. Funkci start() upravte dle následujícího kódu:
// běh traderu
int start()
{
// pokud nemáme otevřený, žádný pokyn
// zkontrolujeme, zda existuje nějaký signál
if(ticketId == -1)
{
// zkontrolujeme signály pro otevření
checkOpenSignals();
}
else
{
// pokud je pokyn stále otevřený
// zkontrolujeme signály pro zavření
if(IsTicketOpen(ticketId))
{
checkCloseSignals();
// pokud byl pokyn zavřen
// zkontrolujeme signály pro otevření
if(ticketId == -1)
{
checkOpenSignals();
}
else
{
// jinak provedeme případné posunutí SL
TrailStopLoss(ticketId, TrailStopLossPips, StopLossPips, true);
}
}
else
{
// pokyn již není otevřen
// zkontrolujeme signály pro otevření
ticketId = -1;
checkOpenSignals();
}
}
return(0);
}
Tělo funkce start() nedělá nic jiného, než že volá kontrolní metody pro otevření či zavření pokynu a v případě otevřeného pokynu posouvá cenu stoploss, je-li ten správný čas.
Tím jsme napsali kompletní strategii. Provedeme kompilaci kódu a zkontrolujeme, jestli v něm nemáme chyby. Pokud ano, je třeba kód opravit na místě, kde je chyba. Po úspěšné kompilaci je třeba naší strategii otestovat.
Otestování strategie
Strategii budeme testovat v Testeru strategií v platformě MetaTrader (viz. kapitola Tester strategií). V seznamu strategií vybereme naší strategii (MyEA – pokud jste použili tento název) a případně v testeru upravíme nastavení nebo vlastnosti strategie. Ve vlastnostech můžeme nastavit vstupní parametry, které jsme definovali na začátku skriptu. V nastavení můžeme změnit pro jaký timeframe grafu chceme strategii testovat a případně můžeme vybrat rozsah testovacích dat výběrem datumu od a do. Kliknutím na tlačítko Začátek spustíme test strategie. Po dokončení můžeme vidět výsledky a provést analýzu strategie. Vzhledem k různosti brokerů (mezi mým a vaším) se mohou výsledky strategie na následujících obrázcích lišit, proto je berte pouze jako ilustrativní.
Strategii jsem použil pro obchodování na měnovém páru EURUSD, pro timeframe M5 a pak pro M15. U první M5 byly data z 1.9.2010 až 17.9.2010, u druhé M5 a M15 je časový rozsah 1.6.2010 až 17.9.2010.
Když se nyní podíváme, tak první M5 graf ukazuje, že strategie je prodělečná, když se jedná o krátký časový úsek. Ale vzhledem k obchodování na M5 bych osobně čekal výdělek než propad, ostatně je to mým cílem.
Na druhém grafu M5 je již vidět, že strategie umí také něco vydělat, ale nepůsobí na mě dojmem, že bych ji mohl věřit. Jednak profit není tak velký a jsou tam časté propady.
Graf M15 je již ničivý a o své konto bych se s touto strategií bál.
Nyní je třeba provést analýzu, co je špatně. Podívám se na graf a najdu výrazné propady, hlavně ty dlouhé neboli série propadů. Dvojklikem na čáru grafu se přepnu na záložku Výsledky. Projdu si průběh a zjistím, že zde byla série nákupů uzavřených stoplossem. Dvojklikem na pokyn se přepnu do hlavního okna s grafem (viz. obrázek). Zde již vidím, že skutečně zde strategie zahájila opakovaně nákupy v protisměru, než šel trh.
Nabízí se otázka co tedy s tím? Je strategie kompletně špatně nebo má určitý potenciál? Potřebuji údaje z dalšího indikátoru nebo ne? Stačí mi kombinovat strategii s více timeframy současně, např. primárně obchoduji na M5, ale filtrování špatných vstupů je odkázáno ve spolupráci s M15? To již jsou otázky mimo rámec tohoto seriálu.
Každý z vás má určitě jinou strategii určující vstupování do trhu a určitě úspěšnou, jenom ji nyní potřebujete zkódovat. Proto na závěr shrnu obecný postup při vývoji:
- - Nejprve provést analýzu jednotlivých kroků určujících vstupování a vystupování do pozic a z pozic. Počítač nemá váš mozek a reaguje pouze na instrukce. Klidně si tyto postupy napište do kódu formou komentářů, usnadní vám to pozdější hledání a vzpomínání, co jste vlastně chtěli v kódu napsat.
- - Dle scénáře si napsat funkce vracející hodnoty jednotlivých indikátorů, pokud je strategie uvažuje.
- - Napsat jiné pomocné funkce, které vám usnadní různé výpočty či rozhodování na základě jiných vstupních dat. V programování platí, že cokoli se v kódu opakuje a je použito na více místech, je již signál pro umístění takovéhoto kódu zvlášť do funkce. Zmenšíte tím délku celého kódu a zpřehledníte čitelnost.
- - Napsat funkce zjišťující signály pro otevření a zavření pokynu.
- - Upravit metodu start() tak, aby strategie mohla plně fungovat.
K dispozici již máte knihovnu funkcí z předchozího dílu, která vám ušetří čas při vývoji. Je možné ji upravovat pro své potřeby.
Závěr
Pokud existují některé nejasnosti, které vznikly během tohoto seriálu, případně pokud vám v seriálu něco chybí, můžete se na mne obrátit formou diskuze, případně na adresu geafer@centrum.cz. Bude-li v mé moci, rád odpovím. Pokud budou podobné dotazy týkající se techniky kódování strategie, můžu napsat článek Tipy a triky apod. Stejně tak uvítám zkušenosti jiných kodérů, cesty pro vývoj strategií jsou různé a tento seriál určitě není dogma. Předem oznamuji, že nebudu psát na žádost kompletní strategie, z časových důvodů nejsem schopen. Tento seriál měl hlavně za úkol vám přiblížit zákulisí jazyka MQL4 při vývoji strategií, ač už jste obchodník, který si dokáže strategii napsat sám, nebo máte člověka, co je schopen.
Přeji vám hodně štěstí a úspěšné obchodování.