MQL 4 COURSE By Coders’ guru www.forex-tsd.com -15 Váš první Expert Advisor - Část 3
V předchozích dvou částech této lekce jsme si představili náš expert advisor a jeho myšlenku. V Příloze 2 jsme studovali funkce Trading, z nichž některé použijeme v expert advisoru dnes. Dnes budeme pokračovat v objasňování zbývajících kódů expert advisoru. Doufám, že je vám myšlenka naší objevitelské mise jasná.
Zde máte kód: //+------------------------------------------------------------------+ //| //| //|
My_First_EA.mq4 | Coders Guru | http://www.forex-tsd.com |
//+------------------------------------------------------------------+ #property copyright "Coders Guru" #property link "http://www.forex-tsd.com" //----vstupní parametry extern double extern double extern double TakeProfit=250.0; Lots=0.1; TrailingStop=35.0; //+------------------------------------------------------------------+ //| expert – inicializační funkce | //+------------------------------------------------------------------+
int init() { //---//---return(0); } //+------------------------------------------------------------------+ //| expert – deinicializační funkce | //+------------------------------------------------------------------+ int deinit() { //---//---return(0); } int Crossed (double line1 , double line2) { static int last_direction = 0; static int current_direction = 0; if(line1>line2)current_direction = 1; //nahoru if(line1
if(current_direction != last_direction) //změněno { last_direction = current_direction; return (last_direction); } else {
return (0); } } //+------------------------------------------------------------------+ //| expert – spouštěcí funkce | //+------------------------------------------------------------------+ int start() { //----
int cnt, ticket, total; double shortEma, longEma;
if(Bars<100) { Print("bars less than 100"); return(0); } if(TakeProfit<10) { Print("TakeProfit less than 10"); return(0); // TakeProfit - kontrola }
shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0); longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0); int isCrossed = Crossed (shortEma,longEma); total = OrdersTotal(); if(total < 1) { if(isCrossed == 1) {
ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point, "My EA",12345,0,Green); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice()); } else Print("Error opening BUY order : ",GetLastError()); return(0); } if(isCrossed == 2) { ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0, Bid-TakeProfit*Point,"My EA",12345,0,Red); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice()); } else Print("Error opening SELL order : ",GetLastError()); return(0); } return(0); } for(cnt=0;cnt
//
exit
// trailing stop - kontrola if(TrailingStop>0) { if(Bid-OrderOpenPrice()>Point*TrailingStop) { if(OrderStopLoss()
//
} // trailing stop - kontrola if(TrailingStop>0) { if((OrderOpenPrice()-Ask)>(Point*TrailingStop)) { if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0)) { OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop, OrderTakeProfit(),0,Red); return(0); } } }
exit
} } } return(0); } //+------------------------------------------------------------------+
int cnt, ticket, total; V tomto řádku jsme deklarovali 3 proměnné typu integer. Jeden řádek jsme pro deklarování použili proto, že jsou stejného typu( u různých datových typů není deklarování na jednom řádku možné).
Poznámka: Pro deklarování mnoha proměnných na jednom řádku tento řádek začnete klíčovým slovem, které indikuje typ proměnných, poté oddělíte identifikátory (jména) proměnných čárkou. Výše uvedený řádek můžete rozdělit do tří, a to takto: int cnt; int ticket; int total; Proměnnou cnt použijeme jako počítadlo v našem “otevřeném příkazu kontrolní smyčky”. Proměnnou ticket použijeme k uchování čísla ticketu vráceného funkcí OrderSend. A proměnnou total k uchování počtu již otevřených příkazů.
double shortEma,longEma;
Opět jsme použili jeden řádek pro deklaraci dvou proměnných double. Tyto proměnné použijeme k uchování hodnot short EMA a long EMA. Jak si (doufám) pamatujete z předchozí části, použili jsme překřížení pozic short a long EMA jako nákupních a prodejních podmínek a také uzavíracích podmínek.
if(Bars<100)
{ Print("bars less than 100"); return(0); } Přejeme si pracovat s běžným grafem a předpokládáme přitom, že běžný graf musí obsahovat více než
100 svící. To proto, že nedostatečný počet svící neumožní EMA indikátorům správnou práci. Počet svící dostaneme v grafu použitím funkce Bars a zakřížkováním čísla zjistíme, zda je počet nižší než 100 či nikoliv. Pokud je nižší než 100, učiníme 2 věci: Sdělíme uživateli, co je špatně, pomocí zprávy v protokolu Expertu "bars less than 100" (obr. 1). Pak zrušíme funkci start řádkem retrun(0);
Tímto způsobem odmítneme práci s méně než 100 svícemi v grafu.
Obr. 1 – protokol Expert
if(TakeProfit<10)
{ Print("TakeProfit less than 10"); return(0); // check TakeProfit } S nedostatečnými údaji hodnoty TakeProfit si příliš pracovat nepřejeme. Proměnná TakeProfit je externí proměnnou, což znamená, že uživatel může změnit její výchozí hodnotu z okna properties expert advisoru. Přejeme si, aby náš expert advisor ochránil uživatele před jeho špatnými volbami. Předpokládáme, že jakákoliv hodnota nižší než 10 u proměnné TakeProfit bude špatnou volbou, a proto jsme zkontrolovali hodnotu TakeProfit , aby uživatel poznal, zda je hodnota nižší než 10 nebo ne.
Pokud je nižší než 10, budeme uživatele informovat, co je špatně, vyobrazením zprávy "TakeProfit less than 10", a zrušíme funkci start příkazem return(0) .
shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0); longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0);
Dobrá tedy, vše je OK, svící v grafu bylo více než 100 a hodnota TakeProfit , kterou dodal uživatel byla více než 10.
Nyní si přejeme propočítat short a long EMA aktuální svíce. Použijeme zabudovaný technický indikátor MQL4 – iMA, který vypočítá indikátor pohyblivého průměru. Zde se musím na několik minut pozastavit, abych prozradil více podrobností o funkci iMA. iMA: Syntax:
Popis: Funkce iMA vypočítává indikátor pohyblivého průměru a vrací jeho hodnotu (datový typ double). Poznámka: Pohyblivý průměr je průměr ceny určité měny za určitý časový interval (ve dnech, hodinách, minutách atd.). Parametry: Funkce obsahuje 7 parametrů: Symbol string: Jméno symbolu měnového páru pro váš obchod (Např.: EURUSD a USDJPY). Pokud si přejete použít aktuální symbol, jako parametr použijte NULL.
int timeframe: Časový rámec budete chtít použít pro výpočet pohyblivého průměru. Použít můžete jednu z těchto hodnot časového rámce:
Konstanta PERIOD_M1 PERIOD_M5 PERIOD_M15 PERIOD_M30 PERIOD_H1 PERIOD_H4 PERIOD_D1 PERIOD_W1 PERIOD_MN1
Hodnota 1 5 15 30 60 240 1440 10080 43200
Popis 1 minuta. 5 minut. 15 minut. 30 minut. 1 hodina. 4 hodiny. Denně. Týdně. Měsíčně.
0 (zero)
0
Čaový rámec použitý v grafu.
Pokud si přejete použít aktuální časový rámec, použijte jako parametr 0.
Poznámka: Můžete použít hodnotu celého čísla periody nebo jméno její konstanty. Např. řádek: iMA(NULL, PERIOD_H4,8,0,MODE_EMA,PRICE_CLOSE,0); je roven iMA(NULL,240,8,0,MODE_EMA,PRICE_CLOSE,0); Doporučuje se však používání jména konstanty, aby byl váš kód jasnější. int period: Počet dní, které si přejete použít pro výpočet pohyblivého průměru. int ma_shift: Počet svící, o který chcete přesunout čáru pohyblivého průměru od začátku grafu: 0 znamená žádný přesun (Figure 2) Pozitivní hodnota přesune čáru doprava (Obr. 3) Negativní hodnota přenese čáru doleva (Obr. 4) int ma_method: Metoda, kterou použijete pro výpočet pohyblivého průměru. Může se jednat o jednu z těchto hodnot:
Konstanta MODE_SMA MODE_EMA MODE_SMMA MODE_LWMA
Hodnota 0 1 2 3
Popis Jednoduchý pohyblivý průměr. Exponenciální pohyblivý průměr. Vyhlazený pohyblivý průměr. Lineárně vyvážený pohyblivý průměr.
int applied_price: Cena, kterou si přejete použít pro výpočet pohyblivého průměru: Může se jednat o tyto hodnoty:
Konstanta PRICE_CLOSE PRICE_OPEN PRICE_HIGH PRICE_LOW PRICE_MEDIAN PRICE_TYPICAL PRICE_WEIGHTED
Hodnota 0 1 2 3 4 5 6
Popis Zavírací cena. Otevírací cena. Nejvyšší cena. Nejnižší cena. Median price, (high+low)/2. Typical price, (high+low+close)/3. Vyvážená uzavírací cena, (high+low+close+close)/4.
int shift: Počet svící (odpovídající aktuální svíci), které použijete pro výpočet pohyblivého průměru. Použijte 0 pro aktuální svíci.
Obr. 2 : ma_ shift = 0
Obr. 3: ma_shift = 10
Obr. 4: ma_shift = -10
shortEma = iMA(NULL,0,8,0,MODE_EMA,PRICE_CLOSE,0); longEma = iMA(NULL,0,13,0,MODE_EMA,PRICE_CLOSE,0); Nyní již znáte význam výše uvedených řádků. Proměnné shortEma jsme přiřadili hodnotu: 8 dní - closing price – na základě exponenciálního pohyblivého průměru aktuální svíce. Ta může být stručně nazvána 8EMA A proměnné longEma jsme přiřadili hodnotu: 13 dní - closing price – na základě exponenciálního pohyblivého průměru aktuální svíce. Ta může být stručně nazvána 13EMA int isCrossed = Crossed (shortEma,longEma);
Poznámka: Funkce Crossed přebírá dvě hodnoty typu double jako parametry a vrací hodnotu celého čísla (integer). První parametr je hodnota prvního řádku, který si přejeme monitorovat (v našem případě short EMA) a druhý parametr je hodnota druhé funkce, určené k monitorování (v našem případě long EMA). Funkce bude monitorovat oba řádky pokaždé, když ji vyvoláme uložením směru obou linií ve statických proměnných k zapamatování jejich stavu mezi opakovanými aktivacemi. Vrátí hodnotu 0, pokud nedošlo k žádné změně v uložených směrech. Vrátí hodnotu 1, pokud došlo ke změně směru (linie se vzájemně protnuly) a první linie je nad druhou. Vrátí hodnotu 2, pokud se směr změnil (linie se vzájemně protnuly) a první linie je pod druhou.
Zde jsme deklarovali proměnnou integer isCrossed k udržení vratné hodnoty funkce Crossed. Tuto hodnotu použijeme pro příkazy otevírání a uzavírání. .
total = OrdersTotal(); if(total
<
1)
{ ……. } Přiřadili jsme vratnou hodnotu OrdersTotal k proměnné total. Poznámka: Funkce OrdersTotal vrací počet otevřených a očekávaných příkazů. Pokud je hodnota 0, znamená to, že se zde žádné otevřené příkazy nevyskytují (ani očekávané nebo příkazy trhu). Viz. dodatek 2 Poté jsme zakřížkovali počet (celkový) pro vyhledání, zda se zde již vyskytly otevřené příkazy či nikoliv. if bude pracovat pouze pokud je celková hodnota méně než 1, což znamená, že se zde žádný již otevřený příkaz nevyskytuje.
if(isCrossed == 1) { ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,3,0,Ask+TakeProfit*Point, "My EA",12345,0,Green); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("BUY order opened : ",OrderOpenPrice()); } else Print("Error opening BUY order : ",GetLastError()); return(0); } V případě, že shortEma protnulo longEma a shortEma je nad longEma, budeme nyní nakupovat. Pro nákup a prodej používáme funkci OrderSend.
Poznámka: Funkce OrderSend se používá pro otevření nákup/prodej nebo očekávané příkazy. Vrací číslo ticketu příkazu v případě úspěchu a -1 v případě selhání.
Syntaxe: int OrderSend( string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=CLR_NONE)
Viz. příloha 2 Toto jsou parametry, které používáme pro naši funkci OrderSend: symbol: Funkci Symbol používáme k získání jména symbolu aktuální měny a předání funkci OrderSend.
cmd: OP_BUY používáme proto, že si přejeme otevření pozice Buy.
volume: Použijeme hodnotu Lots , která byla dodána.
price: Funkci Ask použijeme k získáni aktuální cenové nabídky a předáme ji funkci OrderSend.
slippage: Pro funkci slippage používáme hodnotu 3.
stoploss: Zde je hodnota 0 což znamená, že se zde žádný příkaz stoploss nevyskyuje.
takeprofit: Hodnotu TakeProfit jsme vynásobili, uživatelem byla dodána vratná hodnota funkce Point a přidán výsledek ceny Ask.
Poznámka: Funkce Point vrací velikost bodu aktuálního symbolu měny. Např.: pokud obchodujete EURUSD, hodnota point = 0.0001 a pokud obchodujete EURJPY, hodnota point by měla být 0.01 takže musíte zaměnit vaše hodnoty stoploss a takeprofit za points před jejich použitím s funkcemi OrderSend nebo OrderModify. comment:
U tohoto komentáře použijeme "My EA" řetězec magic: Použijeme číslo 12345 jako číslo magic.
expiration: Datum nastavení hodnoty expiration jsme nepoužili, hodnota je tedy 0.
arrow_color: Nastavíme barvu otevírací šipky na zelenou Green (protože máme rádi peníze a peníze jsou zelené) Funkce OrderSend vrátí číslo ticketu příkazu, pokud je úspěšná, takže ji zkontrolujeme podle tohoto řádku:
if(ticket>0)
Funkci OrderSelect jsme použili pro zvolení příkazu podle čísla ticketu, to proto, že předtím jsme použili funkci OrderOpenPrice , která vrací otevření ceny zvoleného příkazu. Všechno je v pořádku, OrderSend vrátil odpovídající číslo ticketu (vyšší než 0) a OrderSelect úspěšně zvolil příkaz. Nastal tedy čas sdělit tuto dobrou zprávu uživateli jejím vyobrazením v podobě textu „BUY order opened : " plus otevření ceny příkazu. Poznámka: Pro podrobnější informace ohledně OrderSelect a OrderOpenPrice nahlédněte do přílohy 2
V opačném případě, pokud OrderSend vrátil hodnotu –1, což znamená, že během otevírání příkazu se vyskytla chyba, musíme tuto špatnou zprávu sdělit uživateli prostřednictvím vyobrazení textu: "Error opening BUY order : ", plus chybové číslo vrácené funkcí GetLastError. V tomto případě musíme zrušit funkci start použitím řádku return(0).
if(isCrossed == 2) {
ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,3,0, Bid-TakeProfit*Point,"My EA",12345,0,Red); if(ticket>0) { if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES)) Print("SELL order opened : ",OrderOpenPrice());
} else Print("Error opening SELL order : ",GetLastError()); return(0); }
Zde je opačný scénář, kdy shortEma protne longEma a shortEma je pod the longEma, takže teď budeme prodávat. Použijeme funkci OrderSend pro otevření příkazu Sell. Odhadnete, jaký je rozdíl mezi parametry Buy a Sell ve funkci OrderSend? Správně! Za odměnu si zasloužíte 100 bodů. Parametry se nezměnily: symbol je stejný volume je stejný slippage je stejný stoploss je stejný comment je stejný magic je stejný expiration je stejný
Tyto parametry byly změněny (za ně dostanete odměnu):
cmd: Použili jsme OP_SELL , protože si přejeme otevření pozice Sell.
price: Použili jsme funkci Bid pro získání aktuální cenové nabídky a její přenesení do funkce OrderSend.
takeprofit: Vynásobili jsme hodnotu TakeProfit , která byla poskytnuta uživatelem, vratnou hodnotou funkce Point a odečetli výsledek z ceny Bid price.
arrow_color: Nastavili jsme barvu otevírací šipky na červenou Red (Máme sice rádi peníze a barva peněz je zelená, pro prodej však potřebujeme barvu jinou ).
Můžeme stručně zopakovat, co se stane po vyvolání funkce OrderSend s výše uvedenými parametry:
OrderSend vrací číslo ticketu, pokud byla transakce úspěšná. Zkontrolujeme toto číslo, abychom zjistili, zda je hodnota vyšší než 0, což znamená, že nedošlo k žádným chybám. Použili jsme OrderSelect pro volbu příkazu před použitím OrderOpenPrice. Vše je OK, sdělíme tedy tuto dobrou zprávu uživateli vyobrazením textu "Sell order opened: " plus cena otevřeného příkazu.
Jinak, když funkce OrderSend vrátí hodnotu -1, sdělíme tuto špatnou zprávu uživateli vyobrazením textu: "Error opening SELL order: ", plus chybové číslo a zrušení funkce start řádkem (0).
-Počkejte chvíli! (říkáte). -Všiml jsem si, že se zde vyskytuje řádek po posledním bloku kódu if , který byl právě vyložen. -Kde? (říkám já). -Zde to je:
return(0);
Ano! Výborně, tentokrát však odměnu nedostanete. Podívejte se pozorně na tento blok (závorky): if(total < 1) { if(isCrossed == 1) { ..... } if(isCrossed == 2) {
..... } return(0); � Right! }
Pokud shorEma protíná longEma směrem nahoru, otevře se příkaz Buy. Pokud shorEma protíná longEma směrem dolů, otevře se příkaz Sell.
Co když nedošlo ještě k protnutí? Tím nastává práce tohoto řádku. Pokud nedošlo k protnutí, zrušíme funkci start (V případě, že isCrossed se nerovná 1 nebo 2).
Nyní jsme připraveni k otevření pozic Buy a Sell.
V následující části budeme objasňovat zbývající část kódu a podrobně prodiskutujeme MetaTrader Strategy Tester. Doufám, že vás lekce bavila. Velmi uvítám jakékoliv dotazy nebo připomínky. Coders’ Guru 24-12-2005