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() //---- //---- //+------------------------------------------------------------------+ // expert deinicializační funkce //+------------------------------------------------------------------+ int deinit() //---- //---- 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<line2)current_direction = 2; //dolů 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"); if(takeprofit<10) Print("TakeProfit less than 10"); // 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()); 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()); for(cnt=0;cnt<total;cnt++) OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES); if(ordertype()<=op_sell && OrderSymbol()==Symbol()) if(ordertype()==op_buy) // long position je otevřena // měla by být uzavřena? if(iscrossed == 2) OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // uzavření pozice // exit
// trailing stop - kontrola else // přechod do pozice short // měla by bát uzavřena? if(trailingstop>0) if(bid-orderopenprice()>point*trailingstop) if(orderstoploss()<bid-point*trailingstop) OrderModify(OrderTicket(),OrderOpenPrice(),Bid- Point*TrailingStop,OrderTakeProfit(),0,Green); if(iscrossed == 1) OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // uzavření pozice // trailing stop - kontrola // exit if(trailingstop>0) if((orderopenprice()-ask)>(point*trailingstop)) (OrderStopLoss()==0)) if((orderstoploss()>(ask+point*trailingstop)) OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop, OrderTakeProfit(),0,Red);
//+------------------------------------------------------------------+ 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"); 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"); // 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 Hodnota Popis PERIOD_M1 1 1 minuta. PERIOD_M5 5 5 minut. PERIOD_M15 15 15 minut. PERIOD_M30 30 30 minut. PERIOD_H1 60 1 hodina. PERIOD_H4 240 4 hodiny. PERIOD_D1 1440 Denně. PERIOD_W1 10080 Týdně. PERIOD_MN1 43200 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 Hodnota Popis MODE_SMA 0 Jednoduchý pohyblivý průměr. MODE_EMA 1 Exponenciální pohyblivý průměr. MODE_SMMA 2 Vyhlazený pohyblivý průměr. MODE_LWMA 3 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 Hodnota Popis PRICE_CLOSE 0 Zavírací cena. PRICE_OPEN 1 Otevírací cena. PRICE_HIGH 2 Nejvyšší cena. PRICE_LOW 3 Nejnižší cena. PRICE_MEDIAN 4 Median price, (high+low)/2. PRICE_TYPICAL 5 Typical price, (high+low+close)/3. PRICE_WEIGHTED 6 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()); 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()); 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: 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)... h 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