thumbnail
Obchodní systémy

Kompletní návod na programování v MQL

Co je to automatický obchodní systém? Co je to MetaQuotes Language a k čemu se používá? Jaké pojmy musím znát, abych se orientoval v těchto materiálech a mohl zkoušet psát první programy? Jak je to s verzemi MQL a MetaTraderu a kompatibilitou programů?

MQL - Automatické obchodní systémy

Pokud se zajímáte o obchodování, jistě jste už narazili na pojem „automatický obchodní systém“, zkráceně AOS. Díky těmto systémům mají obchodníci mnohem širší možnosti obchodování. AOS může běžet nepřetržitě ve dne v noci a neunaví se. Stejně tak může obchodovat desítky trhů naráz, nemá emoce a dělá pouze to, co má dovoleno. Nikdy se nesplete, neudělá nic navíc, žádný obchod nevynechá. Tyto vlastnosti nemusejí být vždy jen výhodou, ale jelikož čtete tyto řádky, zřejmě jste se pro AOS již rozhodli. Automatický obchodní systém představuje algoritmus napsaný v určitém programovacím jazyce, který má za úkol za předepsaných podmínek otevírat, uzavírat a řídit obchodní pozice nebo případně provádět i jiné úkoly. Není to nic jiného než desítky, stovky nebo i tisíce řádků textu v kódové řeči, které si obchodní platforma přeloží do své řeči a na jejich základě dělá to, co kód říká. Dobrá zpráva je, že máme-li k dispozici platformu, můžeme takovéto systémy obvykle bez dalších instalací a poplatků tvořit a testovat i my. Jsou potřeba jen znalosti programovacího jazyka. Každá obchodní platforma má obvykle svůj vlastní, šitý na míru. My se v této sérii budeme zabývat programovacím jazykem nejpopulárnějšího obchodního softwaru MetaTrader 4. Jazyk se nazývá MetaQuotes Language, zkráceně MQL.

MetaQuotes Language

MQL je programovací jazyk z dílny společnosti MetaQuotes, která je zároveň provozovatelem rozšířené obchodní platformy MetaTrader určené k obchodování CFD derivátů. Modul pro tvorbu a úpravy automatických obchodních systémů, ale také indikátorů a dalších programů určených pro běh v MetaTraderu, je dodáván přímo s platformou a nazývá se MetaEditor. Pokud tedy máte stažený a nainstalovaný MetaTrader, automaticky máte i MetaEditor. Platí to pro verzi 4 i 5. MetaEditor můžete z platformy spustit v nabídce Nástroje – Editor jazyka MetaQuotes anebo přímo z této ikony v horním panelu nástrojů:

O jednotlivých součástech MetaEditoru si řekneme později.

Základní pojmy

Z programátorského hlediska je MQL jazykem odvozeným od jazyka C++. Znalost C++ je tedy výhodou, ale rozhodně není problém se naučit programovat v MQL bez jakýchkoli předchozích programátorských zkušeností. Nyní shrneme některé důležité základní pojmy, se kterými se budete dále setkávat a které jsou podstatné pro porozumění. Exekuce = provedení algoritmu (programu) terminálem MetaTrader. Při exekuci terminál postupně „čte“ kód programu a podle toho koná příslušné akce v terminálu MetaTraderu. Kontrola = ta část programu, která se právě exekuuje. Jinými slovy ta část kódu, kterou terminál aktuálně čte a provádí. Tick = nová příchozí cena. Většina programů se exekuuje právě při každém novém ticku. Komentář = část programu, která se neexekuuje. V kódu se jedná o tu část řádku, která začíná dvěma lomítky za sebou. MetaEditor nám tyto části pro přehlednost obarví šedě a při exekuci je přeskakuje. Chceme-li okomentovat více řádků, začátek komentáře označíme jako /* a konec jako */. Možná se to nezdá, ale popisování částí kódu je kriticky důležité, aby se v kódu vyznal nejen kdokoli jiný, ale i autor sám.

Terminál = klasické uživatelské rozhraní MetaTraderu, ve kterém programy spouštíme a který je schopen exekuovat kódy napsané v jazyce MQL. Kompilace = kompilace je poslední fází tvorby programu. Kompilátor v rámci MetaEditoru při této akci zkontroluje kód, zda v něm nejsou chyby, a potom připraví program k provozu v terminálu. Jsou-li v kódu chyby, kompilátor je vypíše a programátor je nucen je opravit.

MQL vs. Metatrader 4 a 5

Nyní krátce něco k rozdílům mezi verzemi MT4 a MT5 a vztahu k MQL. Platformy MetaTrader 4 a MetaTrader 5 jako takové se liší v mnohém, u příslušných programovacích jazyků to však platí jen částečně. Původně měla každá verze svůj vlastní MetaEditor i jazyk (jazyky vycházely ze stejného základu, jen MQL5 byl vývojově dál). Vedle sebe tak existovaly vzájemně nekompatibilní MQL4 a MQL5. Jelikož ale popularita novějšího MT5 na úkor MT4 stále nestoupala, nedělo se tak ani u verzí MQL. Programátoři však chtěli programovat programy, které byly možné jen v MQL5, také do MT4. Tvůrci jazyků tak přistoupili k takzvanému „velkému sjednocení“, které přišlo spolu se sestavením MetaTraderu č. 600. MQL4 doznalo velké aktualizace směrem k MQL5, stejně tak i příslušný MetaEditor, který je již naprosto stejný v obou verzích. Programy napsané v MQL5 však budou v MQL4 fungovat jen za určitých podmínek, neboť spoustu prvků bylo v MT4 z důvodu kompatibility se staršími programy zachováno. Proto zde pro jistotu ujasním, že tato série se zabývá výhradně jazykem MQL4, který má však k MQL5 již velice blízko. Série bude psána jazykem spíše laickým, aby se základy MQL mohl naučit opravdu každý.

MetaEditor

Jak se dostanu do MetaEditoru? Jaké má části a co umí? Můžu si jednotlivá podokna a panely nástrojů přizpůsobit? Můžu některá okna úplně zavřít? MetaEditor je velice jednoduchý, praktický a efektivní. Neobsahuje žádné nadbytečné funkcionality. Pro leckoho může být výhodou, že Metaeditor byl nedávno přeložen také do češtiny. Na druhou stranu, angličtina hraje hlavní roli přímo v jazyce MQL, takže její znalost se určitě vyplatí. Pracovní prostředí MetaEditoru lze rozdělit do pěti částí.

Horní panel nástrojů

Tyto panely nástrojů obsahují tradiční programovou nabídku a nabídku vztahující se k aktivnímu programu (aktivnímu v hlavním editoru). Kompletní nabídka se skrývá v jednotlivých roletkách File, Edit atd., „vypíchnuty“ jsou potom nejpoužívanější nástroje ve formě ikon. Zleva se ve výchozím nastavení jedná o blok nástrojů pro práci se soubory, dále o dvě ikony pro zapínání/vypínání navigačního modulu a spodního Toolboxu a ostatní ikony se již týkají aktivního programu, se kterým pracujete. Skladbu ikon si můžete přizpůsobit k obrazu svému po kliknutí pravým tlačítkem na některou z nich a volbě Customize (Upravit). Co konkrétní ikona dělá se vám zobrazí v tooltipu po najetí na ní anebo současně ve stavovém řádku ve spodní části MetaEditoru. Úplně vpravo je potom velice důležitý nástroj - vyhledávací pole. Pod ikonou s ozubeným kolem si můžete vybrat, zda chcete zadaný výraz hledat pouze v aktuálním dokumentu a/nebo ve všech souborech a/nebo v celé komunitě MQL. Pokud máte zaškrtnutou volbu hledání v komunitě MQL, po vyhledání se otevřou příslušné www stránky. Výsledky ostatního vyhledávání naleznete v modulu Toolbox na záložce Search. Vyhledávání nepochybně využijete při řešení různých programátorských úkolů, zvláště zezačátku. Komunita MQL je však pouze v angličtině a ruštině.

Navigator

Modul Navigator slouží pro procházení souborové struktury, která se při „velkém sjednocení“ verzí MQL přesunula do poněkud složitější cesty, obecně pro Windows C:Users“Uživatel“AppDataRoaming MetaQuotesTerminal“kód_instance“MQL4. Zde byste měli nalézt všechny zdrojové soubory programů v MQL pro konkrétní instanci Metatraderu. Tyto pak můžete prohlížet a upravovat přímo v editoru. Modul lze podle potřeby vypínat a zapínat příslušnou ikonou nebo nabídkou v hlavním panelu nástrojů.

Samotný editor pro psaní programů

Jedná se samozřejmě o to největší okno. Editor jako takový toho příliš nenabízí. De facto je to jen textový editor, ve kterém píšete a upravujete aktivní program. Vlevo je užitečné počítadlo řádků. Kliknutím na číslo řádku si konkrétní řádek můžete označit, abyste jej později snadno vyhledali. Pokud máte více otevřených programů, pak mezi nimi můžete přepínat pomocí záložek v horní části editoru.

Toolbox (Panel nástrojů)

Toolbox nabízí dalších několik užitečných nástrojů, mezi kterými můžete přepínat v jeho spodní části. Záložka Errors (Chyby) slouží pro přehled a popis chyb v programu při jeho kompilaci. Chybovými hlášeními se bude zabývat jeden z pozdějších dílů série. Search (Hledat) zobrazí výsledky vyhledávání v dokumentu nebo v souborech. Articles (Články) zobrazuje nejnovější články na stránkách komunity MQL. Po dvojkliku se otevře příslušný článek na webu mql5.com V záložce Code Base (Kódová databáze) je k dispozici seznam nových programů, které sdíleli členové komunity MQL na jejich stránky. Po rozkliknutí některé z položek se opět otevře prohlížeč s příslušnou stránkou. Sekce Journal (Deník) slouží jako programový log.

Stavový řádek

Stavový řádek sice zaujímá jediný řádek v samotné spodní části MetaEditoru, ale poskytuje velmi cenné informace. V jeho levé části po najetí na určitou ikonu nebo nabídku zobrazí, co tato ikona nebo nabídka dělá. V pravé části potom vidíte, v jaké části programu se aktuálně nachází váš kurzor, tedy v jakém řádku a sloupci. Úplně vpravo je ještě zkratka typu psaní. INS pro klasické vkládání, OVR pro přepisování. Mezi těmito módy přepnete klávesou Insert, stejně jako v běžných textových editorech. Jelikož už víme o MetaEditoru vše, můžeme začít tvořit první program.

Struktura programu

Jaké jsou části programu? Liší se tyto části dle typu programu? Proč je důležité kódy dělit na logické části? Jak se mezi částmi pohybuje kontrola programu? Každý program můžeme rozdělit do několika logických částí. Jejich počet a použití závisí na typu a účelu daného programu. Základní schéma s částmi, které obsahuje většina programů, nabízí následující diagram. Pod diagramem je vysvětlen postup exekuce programu tak, jak ji počítač provádí. Poté jsou rozebrány jednotlivé hlavní části programu. Možná se to zdá jako zbytečná teorie, poznáte však sami, že znalost toho, jak se co exekuuje, je velice důležitá.

Struktura běžného programu

Struktura běžného programu

Postup exekuce programu

Obvyklý postup exekuce programu mezi jednotlivými částmi naznačuje červená linie na diagramu. Jak již vyplynulo z minulého dílu, program nejdříve přečte hlavičku a následně exekuuje funkci OnInit(), je-li přítomna. Poté se přesune k hlavní speciální funkci, která se liší dle typu programu, jak jsme viděli v minulém díle. Je-li programem skript, speciální funkce OnStart() se vykoná pouze jednou. V ostatních případech se hlavní speciální funkce vykonává pořád dokola, dokud nepřijde pokyn k ukončení programu. Ten může přijít buďto přímo z nějaké části kódu, nebo od uživatele programu. Jakmile se tak stane, exekuce jde na funkci OnDeinit(), je-li přítomna, a následně program končí. Žluté linie označují možné volání uživatelských funkcí z hlavních funkcí. Uživatelskými funkcemi se budeme blíže zabývat později. Jsou to části kódu, které mají za cíl vykonat určitý úkol. V kódu jsou umístěny mimo ostatní funkce, obvykle pod celým kódem. Jak je vidět, funkce mohou být volány ze všech speciálních funkcí, nejen z hlavní, ale také mezi uživatelskými funkcemi navzájem. Množství uživatelských funkcí v programu není omezeno. Modré šipky označují komunikace jednotlivých funkcí s terminálem. To se může dít například za účelem získání aktuální ceny instrumentu, objemů a dalších informací. V některých případech může program zajišťovat komunikaci i mimo terminál, což označují černé šipky. Jedná se třeba o exekuci příkazů, kdy program komunikuje se serverem brokera, nebo o práci se soubory, kdy se vyměňují informace přímo s operačním systémem.

Hlavička

V hlavičce se obvykle nejprve nachází základní informace o programu, jako verze, autorství, popis atd. Jsou to ty informace, které se nám zobrazí po spuštění programu v Metatraderu. Aby se zobrazily uživateli, v kódu musí vždy začínat modifikátorem #property a názvem daného typu informace (např. copyright, description, version atd.). Dále v hlavičce definujeme globální proměnné. Tyto proměnné budou k dispozici všem funkcím v programu. O proměnných si samozřejmě povíme blíže v dalších částech série. Pokud je před názvem proměnné modifikátor „input“, pak definujeme proměnnou, jejíž hodnotu bude mít možnost zadat sám uživatel programu (v případě skriptu je ale třeba ještě zadat modifikátor #property show_inputs, aby se zobrazilo okno s proměnnými). Dále lze v hlavičce například importovat funkce z jiných souborů knihoven a další činnosti. Podstatou ale je, že v hlavičce není kód k exekuci, pouze prostor pro základní definice. V hlavičce nelze používat operátory a uživatelské funkce.

Hlavička

Příklad hlavičky programu

Speciální funkce OnInit()

Tato funkce obsahuje kód, který bude exekuován jako první. Je to tedy souhrn akcí, které mají být vykonány před spuštěním hlavní části programu. Může se jednat o přípravu grafiky, definici linií indikátoru, přípravné výpočty apod. U indikátoru je funkce přítomna téměř vždy, jinak ani nemusí být potřeba a můžeme ji nechat prázdnou nebo úplně smazat. V takovém případě kontrola přejde z hlavičky rovnou k hlavní speciální funkci. Přestože MetaEditor ji vždy vygeneruje na první místo, v praxi je úplně jedno, kde je v programu umístěna. Kontrola k ní vždy zamíří jako první, je-li v kódu přítomna.

OnInit

Příklad funkce OnInit() v programu typu indikátor, kde definujeme počet a styly linií.

Hlavní speciální funkce

Každý program musí obsahovat speciální funkci, která bude obsahovat hlavní část programu k exekuci. Tedy tu část, která definuje hlavní poslání našeho programu. Konkrétní hlavní speciální funkce se liší dle typu programu. V případě skriptu má název OnStart(), u strategie jde o OnTick() a konečně indikátor má OnCalculate(). Do programů lze přidávat ještě několik dalších speciálních funkcí. O nich bude po ukončení základní série následovat speciální článek, pro základy jich nebude třeba. Název speciální funkce naznačuje, při jaké události se funkce přivolá. OnInit() se exekuuje při inicializaci, OnTick() při příchozím novém ticku apod. V této části programu se obvykle nachází největší část kódu a exekucí této části se rovněž kontrola programu zabývá nejdelší čas. V případě strategie nebo indikátoru se tato část opakuje stále dokola, dokud není program ukončen.

Vlastní uživatelské funkce

Většina trochu složitějších kódů bude z důvodu přehlednosti obsahovat uživatelské funkce. To jsou všechny nepředdefinované funkce, které si programátor nadefinuje v kódu sám. Podrobně se jimi budeme zabývat později. Rovněž je úplně jedno, na kterém místě jsou v kódu umístěny. Obvykle se pro přehlednost dávají na úplný konec. Exekuovány jsou ve chvíli, kdy jsou takzvaně zavolány z jiné části kódu, která se právě exekuuje. Princip volání a konstrukce uživatelských funkcí si ukážeme v dalších dílech, ale určitě není na škodu se již nyní podívat, jak může vypadat.

Vlastní funkce

Příklad programu na výpočet přepony pravoúhlého trojúhelníku, který využívá volání na uživatelskou funkci. Samotné volání uživatelské funkce z hlavní funkce skriptu OnStart() je na řádku č. 6, tělo volané funkce s názvem Pythagoras je na řádcích 14 – 16. Do funkce jsou převedeny parametry A a B, funkce vrací proměnnou C, tedy délku přepony.

Speciální funkce OnDeinit()

Jak plyne z názvu, tato speciální funkce má na starost provést kód při deinicializaci, tedy ukončení programu. Je to tedy soubor akcí, které jsou vykonány, jakmile uživatel nebo terminál dá příkaz k ukončení programu. Může se jednat třeba o vymazání grafických prvků programu, rozloučení s uživatelem apod. Opět nezáleží na pozici v kódu. Jakmile je dokončena exekuce této funkce, program končí. Pokud funkce chybí, stejně jako u OnInit() se program ukončí rovnou po signálu k ukončení.

Příklad jednoduché funkce OnDeinit(), při které jsou smazány veškeré grafické objekty grafu.

Příklad jednoduché funkce OnDeinit(), při které jsou smazány veškeré grafické objekty grafu.

Nový program, typy programů

Jak začnu v MetaEditoru tvořit svůj program? Jaké jsou typy programů a jejich specifika? Který program si mám vybrat? Po popsání jednotlivých prvků MetaEditoru jsme připraveni pustit se do tvorby prvního programu. Tvorbu programu začneme kliknutím na ikonu vlevo v panelu nástrojů nebo na tlačítko se stejným názvem v nabídce File (Soubor). Jako první budeme muset vybrat typ programu, který chceme tvořit. Předvybranou máme volbu Expert Advisor, neboli obchodní strategie, dále můžeme zvolit program typu indikátor nebo skript. MetaEditor nám podle vybraného typu předpřipraví kód programu, ale není to volba nevratná, kód si potom můžeme libovolně změnit, tedy i s typem programu. Poslední tři typy ponecháme dále bez povšimnutí, v tomto seriálu je nebudeme potřebovat. Jedná se o přídavné kódy pro složitější programy. Nyní, když si máme vybrat typ programu, je na místě vysvětlit základní rozdíly mezi strategií, indikátorem a skriptem. Poté tvorbu programu dokončíme.

Wizard

Skript

Skript je nejjednodušším typem programu, který slouží k jednorázovému vykonání nějakého úkolu. Po spuštění se exekuuje pouze jednou a vypne se. Celý kód k exekuci je součástí speciální funkce OnStart(), jiné nemají ve skriptu smysl. Žádnou paniku, o speciálních funkcích si blíže povíme v dalších kapitolách. Příkladem skriptu mohou být programy, které jednorázově vykreslí do grafu nějaký grafický prvek, programy, které otevřou nebo uzavřou pozice s určitými specifiky a podobně. Skript v sobě tedy může obsahovat obchodní funkce, které mohou otevírat nebo uzavírat či modifikovat pozice. Nejdůležitější ovšem je, že celý kód se exekuuje pouze jednou. Jakmile je kód proveden do konce, program končí.

Automatický obchodní systém (Expert Advisor)

Program typu strategie slouží primárně k psaní obchodních systémů, ale může být použit i k tvorbě jiných podpůrných programů, které se mají exekuovat neustále dokola, například informačních. Na rozdíl od skriptu se kód exekuuje pravidelně, obvykle při každém příchozím ticku, tedy při každé nové příchozí ceně (jedinou výjimkou jsou ticky, které přišly v době, kdy ještě nedoběhla předchozí exekuce programu). Hlavní část programu je obvykle součástí speciální funkce OnTick(), přítomny jsou obvykle ještě funkce OnInit() a OnDeinit(), kde je umístěna část kódu, která se exekuuje při spouštění, respektive vypínání strategie. Do programu je možné přidat ještě další speciální funkce, o kterých bude řeč v dalších dílech. Většinu z nich ale pravděpodobně nebudete potřebovat, zvlášť pokud s MQL začínáte. Součástí strategie mohou samozřejmě rovněž být obchodující funkce. Strategie po zapnutí běží, dokud není vypnuta.

Indikátor (Custom indicator)

Program typu indikátor slouží pro tvorbu vlastních grafických indikátorů. Hlavní speciální funkcí tohoto typu je funkce OnCalculate(), která je rovněž exekuována při každé příchozí ceně. I zde bývají časté funkce OnInit() a OnDeinit(), případně některé další ze speciálních funkcí. Na rozdíl od strategie tento typ nemůže obsahovat obchodující funkce. Lépe řečeno může, ale bude je ignorovat. Jakmile víme, který typ programu chceme tvořit, můžeme přejít do další nabídky. V následujícím okně je potřeba program pojmenovat, to je jediné povinné pole. Můžeme také přidat autorství a link na webovou stránku. Do největšího pole lze přidat již známé parametry (proměnné), které budou v programu účinkovat. I zde platí, že všechny (ne)zadané hodnoty kromě názvu lze poté v kódu bez problému upravovat, mazat a přidávat. Pokud jsme na začátku zvolili skript, průvodce novým programem bude v tuto chvíli ukončen. Máme-li v plánu tvořit jiný program, bude po nás průvodce chtít ještě další informace týkající se speciálních funkcí. Pokud s MQL začínáte, tyto nabídky vás vůbec nemusí rozptylovat a můžete je s klidem proklikat tak jak jsou. Budou se hodit až u složitějších programů. Pokud tvoříte indikátor, na konci průvodce ještě můžete zadat plánované linie indikátoru s jejich stylem, barvou a názvem. Rovněž můžete definovat, zda budou v odděleném okně nebo přímo v grafu a případně jestli budou mít minimum a maximum. Opět se ale nejedná o nic, co by nešlo měnit přímo v kódu. Po dokončení průvodce na vás již vyskočí v editoru přednastavený kód, jehož tvar se odvíjí od toho, co jste zadali v průvodci. Abyste začali poznávat určité části kódu shodné všem programům a mohli si začít organizovat kód svého programu, budeme se dále věnovat struktuře programu.

Typy dat

Proč je potřeba rozlišovat data dle jejich typu? Jaké typy dat existují a k čemu se hodí? Jak s daty pracuje počítač? Každý program musí zpracovávat určitá data. Ne všechna jsou však vzájemně srozumitelná. Budeme používat čísel, textů, mnohdy třeba také barev nebo výčtů a s těmito daty budeme provádět různé operace – aritmetické jako sčítání, násobení apod., přiřazovací (proměnným budeme přiřazovat hodnoty), porovnávací a další. Tyto operace však můžeme provádět pouze s daty stejného typu. Jinými slovy zkrátka nemůžeme míchat jablka a hrušky dohromady. K číslu nelze přičítat text a barvu těžko porovnáme s datem. Proto je potřeba striktně rozlišovat, s jakým typem dat pracujeme. Abychom počítači sdělili, s jakým typem dat u konkrétní proměnné nebo funkce pracujeme, používáme v kódu tzv. identifikátory typů dat. Tyto identifikátory vkládáme před proměnnou či funkci při jejím zavedení. O zavádění neboli deklaraci proměnných a o funkcích se dočtete v následujících dílech. Data se v kódu dají dělit do typů uvedených v následující tabulce. Uvedeny jsou jen základní typy, které bohatě stačí pro tvorbu běžných programů. Existují ještě odvozené typy, které však mají většinou jen funkci šetření paměti počítače nebo naopak umožňují vložit extrémní hodnoty. O nich se můžete anglicky dočíst na stránce http://docs.mql4.com/basis/types.

Identifikátor Anglicky Česky
int integer celé číslo
bool logical pravda/nepravda
string string řetězec
double floating-point number desetinné číslo
color color barva
datetime date and time datum a čas
enum enumeration výčet

Není bez zajímavosti, že program při svých výpočtech používá pouze tři typy z těchto všech, a to celá a desetinná čísla a řetězce. Rozdíl od ostatních typů je pouze ve velikosti paměti, kterou počítač pro danou hodnotu alokuje, a ve formě jakou jsou data prezentována uživateli. V čem to vězí se dočtete na následujících řádcích.

Celá čísla (int)

Tento typ dat se hodí pro proměnné a funkce, které ze své podstaty mohou nabývat jen celých čísel. Může jít třeba o počet pozic, počet svíček v grafu, rozdíl mezi cenami v pipech a podobně. Hodnota proměnné tohoto typu je omezena číslem 2 147 483 647 v kladném i záporném spektru. Pokud bychom potřebovali vyšší číslo, je potřeba použít odvozeného datového typu, jako třeba uint nebo long. Příklad zavedení celočíselné proměnné a přiřazení konkrétní hodnoty ukazuje následující řádek. int Cislo = 5;

Hodnoty typu Bool (bool)

Logická hodnota neboli Bool hodnota je datový typ reprezentovaný jednou z hodnot PRAVDA, nebo NEPRAVDA. V kódu lze zapsat jako true a false nebo jako 1 a 0. Z toho plyne, že i tento typ bere počítač jako celé číslo. bool Chyba = true; bool Chyba = 1;

Řetězec (string)

Typ řetězec používáme pro data, do kterých chceme vložit texty neboli řetězce jakýchkoliv znaků. Začátek a konec řetězce musí vždy ohraničovat uvozovky. string Reklama = „Zdolejte Výzvu a spravujte 250 000 CZK obchodního kapitálu“;

Desetinné číslo (double)

Desetinné číslo použijeme samozřejmě tam, kde hodnota není ze své podstaty celočíselná, ale může nabývat i desetinných míst (až 15). double Des_cislo = 15.958645; double Prum_zisk = Celkovy_profit/Pocet_dni;

Barva (color)

Pokud danou proměnnou nebo funkci určíme jako typ barva, hodnota, kterou do ní uložíme, bude počítačem pochopena jako kód konkrétní barvy dle své interní palety barev. Máme tři možnosti, jak mu říci, kterou barvu chceme. Nejjednodušší možnost je napsat ji slovy. Název barvy však musí být jednou z webových barev, které je MQL schopno rozeznat, jejich tabulku i s názvy najdete na adrese https://docs.mql4.com/constants/objectconstants/webcolors. color Cervena = Red; Další možností je celočíselný zápis. Na webu se obvykle používá ve formě takzvaného hexadecimálního kódu. Hexadecimální kód konkrétní barvy vám vrátí každý jednodušší textový nebo grafický editor. Kód vychází z hodnot RGB, tedy z podílu červené, zelené a modré ve tvaru 0xRRGGBB. color Bila = 0xFFFFFF; Použít můžeme i stejnou hodnotu převedenou do desítkové soustavy. color Zelena = 32768; Možností, jak zadat kód barvy, je více, osobně je však považuji za méně praktické, proto vás zde odkážu na příslušné stránky https://book.mql4.com/basics/types#13.

Datum a čas (DateTime)

Tento typ dat slouží pochopitelně k zápisu data a případně ještě času. Zápis konkrétního data bude vždy začínat identifikátorem D´ a následně kódem data a času. Přijímán je jak formát rrrr.mm.dd tak dd.mm.rrrr, na který jsme zvyklí z našich zeměpisných šířek. Vteřiny, minuty a hodiny jsou dobrovolné. Pokud však bude chybět den, měsíc či rok, kompilátor nás upozorní na chybu. datetime Silvestr1 = D'2017.31.12 23:59:59'; datetime Silvestr2 = D'31.12.2017'; I v tomto případě se pro potřeby výpočtů procesoru datum převádí na celé číslo, a to jako počet vteřin, které uplynuly od 1. 1. 1970 00:00:00. Datum a čas 29. 8. 2015 15:44 tedy lze zapsat i takto: datetime Ted = 1440863081; Datum takto nejspíš zadávat nikdy nebudete, ale může se stát, že tam, kde očekáváte datum, na vás v důsledku nějaké chyby v kódu vyskočí takovéto hausumero. Příčinu tedy již budete znát. V praxi se datum explicitně do proměnné zadává málokdy. Obvykle jsou proměnné, které v sobě nesou čas, nějakým způsobem odvozené například od aktuálního času, na který máme funkce TimeCurrent() a TimeLocal(). I tady se bude hodit výše zmíněný přepočet na celočíselný kód, protože například současný čas + dva dny se spočte jednoduše takto: datetime Pozitri = TimeLocal() + 2*24*60*60; Jinými slovy, pozítří = teď + 2(dny)*24(hodin)*60(minut)*60(vteřin). Proč tomu tak je, už nyní víte a dost to usnadní práci s daty.

Výčet (enum)

Výčet je jeden z datových typů, který se v MQL4 objevil až po velkém sjednocení. Tento typ dat slouží k vytvoření skupiny dat stejného typu. Datům můžeme přidělit hodnoty, ale nemusíme, pak budou přiděleny automaticky. Výčet musí být deklarován uvnitř složených závorek. Výčty v základní sérii pravděpodobně potřebovat nebudeme. Již několikrát jsme však narazili na důležitý pojem „proměnná“.

Proměnné

Co je to proměnná a jakou má v programu funkci? Jak proměnnou vytvořím? Jak jí přidělím hodnotu? Na jaké typy lze proměnné dělit? Práci s proměnnými se rozhodně nevyhneme v žádném programu. Proměnná působí jako držitel určité hodnoty, kterou jí přiřadíme. Hodnota proměnné je uložena v paměti počítače, dokud není zase jinou operací změněna. Pomocí proměnných může program uchovávat a prezentovat informace v různých formách. Budete však využívat i proměnné pomocné, se kterými se bude operovat pouze v rámci kódu, takže jejich použití nebude uživateli navenek nikterak znát. Možná z těchto definic nejste zrovna moudří, ale z praktických příkladů vám bude brzy jasné, o co se jedná. Proměnné se dělí podle různých kritérií, ale nejdříve si řekneme, jak takovou proměnnou „vyrobit“, aby to počítač pochopil, a jak jí přidělit hodnotu.

Deklarace

Zavedení proměnné do programu se říká deklarace. Je to jednoduché, pro zavedení stačí znát její název a typ data, který do ní hodláme uložit. O typech dat jsme pojednávali v minulém díle. Zavedení provedeme zápisem identifikátoru datového typu a poté názvu proměnné. Název může obsahovat pouze znaky anglické abecedy, nelze používat speciální znaky včetně například tečky. Jelikož deklarace je operací, na konec je potřeba napsat středník. Proč tomu tak je se dozvíte v dalším díle věnovaném operacím. Zavedení proměnné s názvem „Vyzva“, která má obsahovat celočíselné hodnoty, bude tedy v kódu vypadat takto: int Vyzva; Proměnných stejného typu můžeme deklarovat i více najednou, stačí jejich názvy oddělit čárkou: double Zisk,Ztrata,Prum_zisk,Prum_ztrata,Celk_Profit,Podil; Takto jsme deklarovali šest proměnných, do kterých plánujeme uložit desetinná čísla. Pokud by některé proměnné byly jiného typu, musíme je deklarovat zvlášť. První přiřazení hodnoty proměnné se nazývá inicializace.

Inicializace

Inicializace, tedy první přiřazení hodnoty proměnné se provede jednoduše přiřazovací operací, ve které bude figurovat symbol =. Možností je celá řada. Inicializace může být provedena přímým přiřazením: Ucet = 250000; Nebo výsledkem rovnice, ve které figurují jiné proměnné: A = B + C; Nebo třeba výsledkem funkce či kombinace funkcí: Dnes = TimeToString(TimeCurrent(),TIME_DATE); Takto jsme například do proměnné Dnes vložili aktuální datum serveru brokera převedené na text. Proměnná Dnes však musela být deklarována jako typ řetězec, tedy string. Opět nezapomeňte na středník na konci. Deklaraci a inicializaci lze sloučit do jediné operace na jednom řádku, například takto: int A = 3; color Zluta = Yellow, Limetkova = Lime; Na předchozích dvou řádcích jsme zavedli celočíselnou proměnnou A o hodnotě 3 a barvy Zluta o hodnotě Yellow a Limetkova o hodnotě Lime.

Vlastní vs. předdefinované proměnné

Výše jsme si ukázali tvorbu a práci s vlastními proměnnými. V rámci MQL však existuje i několik proměnných předdefinovaných, které nemusíme deklarovat ani inicializovat. Pokud je v kódu použijeme, vybarví se nám růžově a automaticky v sobě obsahují příslušnou hodnotu. Tyto proměnné v sobě obsahují specifická data o trhu, grafu, na kterém program běží, a o dalších vlastnostech terminálu. Níže naleznete tabulku s těmito proměnnými a hodnotami, které v sobě obsahují. Některé pravděpodobně nikdy nepoužijete, jiné se používají téměř v každém programu.

Proměnná Hodnota
_Digits Počet desetinných míst ceny trhu, na kterém program běží
_Point Velikost bodu trhu, na kterém program běží
_LastError Kód poslední chyby
_Period Časový rámec grafu, na kterém program běží
_RandomSeed Stav generátoru pseudonáhodných čísel
_StopFlag Program stop flag
_Symbol Název symbolu, na kterém program běží
_UninitReason Důvod deinicializace
Ask Poslední známá cena Ask trhu, na kterém program běží
Bars Počet úseček grafu, na kterém program běží
Bid Poslední známá cena Bid trhu, na kterém program běží
Close[] Proměnná typu pole obsahující zavírací ceny všech svíček na grafu
Digits viz _Digits
High[] Proměnná typu pole obsahující nejvyšší ceny všech svíček na grafu
Low[] Proměnná typu pole obsahující nejnižší ceny všech svíček na grafu
Open[] Proměnná typu pole obsahující otevírací ceny všech svíček na grafu
Point viz _Point
Time[] Proměnná typu pole obsahující časy otevření všech svíček na grafu
Volume[] Proměnná typu pole obsahující objemy všech svíček na grafu

Po kliknutí na název proměnné se dostanete na její podrobný popis na stránkách mql.com (v angličtině). Možná jste se pozastavili nad proměnnými typu pole. To je však natolik rozsáhlá a důležitá oblast, že jí bude věnována celá další kapitola. Nyní ještě krátce k rozdělení proměnných na globální a lokální.

Globální vs. lokální proměnné

Zda je daná proměnná globální nebo lokální závisí na tom, ve které části programu je deklarována. Je-li proměnná deklarována v hlavičce kódu mimo těla veškerých funkcí (o struktuře programu jste četli ve čtvrtém díle), jedná se o proměnnou globální. Taková proměnná je k dispozici všem funkcím programu – hlavním i uživatelským. Jinými slovy, lze ji použít se stejným názvem i aktuální hodnotou ve všech částech programu. Tento typ globální proměnné se ale nesmi zaměňovat s globálními proměnnými dostupné dokonce pro celý terminál a všechny jeho další programy. S těmi se pracuje pomocí sady funkcí.  Naopak lokální proměnná je každá proměnná, která je definována uvnitř některé funkce. Ta je potom k dispozici pouze pro tuto funkci. Pokud ji použijeme v jiné funkci, nebude rozpoznána, respektive kompilátor to označí za chybu a donutí vás ji opravit. V této jiné funkci může být klidně zavedena proměnná se stejným názvem, ale její hodnota se bude odvíjet pouze od operací v této funkci. Pokud potřebujete převést hodnotu nějaké proměnné z jedné funkce do jiné, lze to provést pomocí parametrů volání funkce. O nich ale až v kapitole o funkcích. Jak bylo řečeno výše, v další kapitole problematiku proměnných ještě rozšíříme o proměnné typu pole, které se používají u tolik důležitých předdefinovaných proměnných jako Open, High, Low, Close, Time a Volume.

Proměnné typu pole

Co je to proměnná typu pole? Jak se indexují její hodnoty? V čem se liší deklarace a inicializace oproti normální proměnné? Jak vyvolám konkrétní hodnotu ze souboru hodnot? Proč takové proměnné existují a v čem mi usnadní práci? Jednu celou kapitolu je potřeba vyčlenit na proměnné typu pole, zvláště pak na ty předdefinované, které jsou naštěstí jejich nejjednodušším případem. Je tomu tak proto, že tyto proměnné jsou používány pro vyvolání charakteristických hodnot svíček v grafu, které jsou nutné pro tvorbu většiny obchodních systémů a indikátorů.

Proměnná typu pole obecně

Běžná proměnná v sobě může současně nést pouze jedinou hodnotu. Oproti tomu proměnná typu pole v sobě nese celý indexovaný soubor hodnot, který navíc může být až čtyřrozměrný. Díky indexům posléze můžeme vyvolat konkrétní hodnotu ze souboru, kterou zrovna potřebujeme. V jednom okamžiku lze mít přístup pouze k jediné z hodnot souboru. Čím více rozměrů má soubor, tím více souřadnic index má. Je to stejné, jako když hledáme bod na dvourozměrné ploše, třeba na mapě. Taky musíme zadat zeměpisnou délku a šířku. Když ale hledáme bod v třírozměrném prostoru, potřebujeme už souřadnice tři. Oproti zeměpisným souřadnicím by to mohlo být třeba ještě patro budovy. Hezky to ilustruje obrázek ze stránek mql.com:

Obrázek a) ilustruje jednorozměrný soubor hodnot proměnné typu pole, b) dvourozměrný a c) třírozměrný. Pro účely této série nám bude naštěstí stačit práce s jednorozměrnými soubory.

Deklarace a inicializace

Deklarace proměnné typu pole se oproti běžné deklaraci liší jen v jedné věci. Oproti datovému typu a názvu proměnné musí být uveden ještě počet hodnot souboru, a to v hranatých závorkách. Inicializace probíhá rovněž jen mírně odlišně. Chceme-li soubor hodnot vložit ručně, provedeme to jejich výčtem ve složených závorkách. int Rada[4] = {9,8,7,6}; Takto jsme nadeklarovali jednorozměrnou proměnnou Rada celočíselného typu, která obsahuje hodnoty 1, 2, 3 a 4. Indexace probíhá zleva doprava od nuly. Konkrétní hodnota je později v programu vyvolána názvem řady a indexem v hranaté závorce. Vysledek = Rada[1]; Po provedení tohoto přiřazení nabude proměnná Vysledek hodnoty 8, jelikož hodnota proměnné Rada s indexem 1 je 8 (první hodnota, tedy 9, má index 0). Ruční deklarace však není moc praktická hlavně pro obrovské soubory hodnot a moc se nepoužívá. Šikovnější způsob inicializace proměnné typu pole tkví v cyklech. Ty nás čekají až v díle o operátorech, ale přesto si pro ilustraci ukážeme, jak by taková inicializace mohla vypadat. for (x = 0; x < Bars; x++) { Rada[x] = (High[x]+Low[x])/2 } V tomto případě poslouží cyklus for, který funguje tak, že z hlavičky (závorka nahoře) vezme první výraz (x = 0), provede iteraci cyklu (exekuuje kód uvedený ve složený závorce), potom provede třetí výraz v kulaté závorce (x++, což je x+1), provede další iteraci cyklu a takto postupuje stále dokola, dokud je splněna podmínka ve druhém výrazu kulaté závorky (x < Bars). O proměnné Bars jsme si již říkali – jedná se o počet svíček v grafu. Jak vidíme, každá iterace je provedena s x vyšším o jednu hodnotu a poslední iterace je provedena pro x = Bars-1, protože posléze již není splněna podmínka druhého výrazu. Při každé iteraci cyklu je hodnotě proměnné Rada s indexem x přiřazena hodnota, která vyplyne z rovnice (High[x]+Low[x])/2, což není nic jiného, než průměr High a Low svíčky s indexem x. Výsledkem tedy bude jednorozměrná proměnná typu pole „Rada“, která v sobě bude obsahovat soubor průměrů high a low všech svíček v grafu. Tím se krásně dostáváme k velice důležité problematice takzvaných časových řad a jejich indexaci.

Předdefinované proměnné časových řad

Jak jsme si řekli už v minulém díle, jazyk MQL obsahuje několik předdefinovaných proměnných, které není třeba deklarovat ani inicializovat. Některé z nich jsou typu pole, a především pro ně vznikl tento díl. Pro jistotu v následující tabulce zopakuji, o jaké proměnné se jedná.

Proměnná Hodnota
Time Časy otevření všech svíček grafu
Open Otevírací ceny všech svíček grafu
High Nejvyšší ceny všech svíček grafu
Low Nejnižší ceny všech svíček grafu
Close Zavírací ceny všech svíček grafu
Volume Objemy všech svíček grafu

Seznam je vám dost možná povědomý. Pokud totiž v Metatraderu v grafu najedete kurzorem myši na konkrétní svíčku, zobrazí se tooltip právě s těmito jejími specifiky. Stejná specifika také současně vidíte na stavovém řádku vpravo. Díky těmto proměnným můžeme v kódu snadno vyvolat hodnotu jakéhokoliv z těchto specifik jakékoliv svíčky na grafu, kde je program spuštěn. Jak hodnotu vyvolat už víme, teď potřebujeme jen vědět, na základě jakého klíče se tyto hodnoty svíček indexují. Indexace svíček probíhá od nuly vzestupně zprava. To znamená, že aktuální úsečka, která se právě vytváří, má index 0. Poslední hotová úsečka má index 1 atd. Vše ilustruje následující obrázek a tabulka:

Na obrázku je minutový graf, kde čísla nad svíčkami odpovídají jejím indexům. V tabulce jsou potom konkrétní specifika svíček s danými indexy. Z obrázku a tabulky plyne, že chceme-li získat například otevírací cenu aktuální svíčky, potřebujeme hodnotu Open[0], která je aktuálně 1,54370. Volume svíčky s indexem 19 - Volume[19] má aktuální hodnotu 82 a podobně. Pokud se vrátíme k příkladu výše s proměnnou Rada a aplikujeme jej na tuto konkrétní situaci, pak po provedení tohoto výrazu Rada[2] = (High[2]+Low[2])/2 Bude do proměnné Rada s indexem 2 uložena hodnota (1,54352 + 1,54324)/2 = 1,54338. S tímto typem proměnných se setkáme v této sérii ještě mnohokrát, takže pokud jste tématu plně neporozuměli, nevadí, v praxi bude vše jasnější.

Operátory I

Co jsou to operátory? K čemu jsou potřeba? Jaké operátory rozlišujeme? Dle definice jsou operátory části programu, které obsahují předpis konverze dat určitou metodou za určitých podmínek. Tato definice nám ale asi příliš neřekne, a tak jsem opět pro nezdržování zbytečnou teorií a přejití rovnou ke konkrétním operátorům a jejich významu pro program. Analogicky by se operátory daly přirovnat k větám a odstavcům, jestliže celý kód je článek. Každý operátor má svůj specifický úkol. Například přiřazovací operátor daný znakem „=“ má za úkol přiřadit určitou hodnotu dané proměnné. Podmínkové operátory mají za úkol rozhodovat o pravdě nebo nepravdě určitého výrazu a podle toho konat nebo nekonat další akce. Cyklické operátory zase opakují exekuci stejného kódu dle zadaných určitých podmínek. Jednoduché operátory jako sčítání, přiřazování, porovnávání atd. už známe z minulého dílu, tento proto bude věnován především těm složeným, jejichž pochopení je klíčové pro tvorbu efektivních programů.

Jednoduchý vs. složený

Jednoduchý operátor je ukončený středníkem. Díky středníku počítač pozná, kde končí jeden operátor (jedna operace) a začíná další. Dle analogie z prvního odstavce je to tedy pomyslná jednoduchá věta. Příkladem může být zmiňované přiřazení hodnoty, volání funkce, sečtení dvou hodnot apod. Většinu operací, které provádíme pomocí jednoduchých operátorů, jsme již shrnuli v minulém díle. Na další se podíváme později. Naproti tomu složený operátor lze přirovnat k celému odstavci. Dělí se na takzvanou hlavičku (nezaměňovat s hlavičkou celého kódu) a tělo operátoru. V hlavičce jsou definovány vlastnosti operátoru – obvykle se jedná o podmínky, za kterých se bude exekuovat tělo. Tělo je složeno většinou z několika jednoduchých operátorů oddělených středníkem (věty odstavce). Celé tělo je potom uloženo ve složených závorkách tak, aby program poznal, kde skupina operátorů přidružená hlavičce začíná a končí. Mezi nejpoužívanější operátory patří podmínkové a cyklické. Složené operátory jsou v programu vyznačeny jasně modrou barvou, podobně jako zkratky datových typů. Ve zbytku tohoto dílu a v díle dalším si představíme a vysvětlíme všechny hlavní operátory.

Podmínkový operátor if-else

Operátor if-else je nejpoužívanějším složeným operátorem. Pomocí něho můžeme vytvořit rozhodovací mechanismus, který vykoná nějakou akci (námi zadanou skupinu operací), pokud je splněna určitá podmínka a případně vykoná jinou akci, pokud tato podmínka splněna není. Obecně má operátor if-else následující tvar: if („podmínka“) { blok operátorů k vyplnění, pokud je podmínka splněna; } else { blok operátorů k vyplnění, pokud podmínka splněna není; }   Rozlišujeme tři struktury podmínkového operátoru if-else podle toho, jaké části obsahuje. Níže jsou všechny představeny na jednoduchém příkladu. Začneme základním typem. if (Bid > Level) { Alert("Cena překročila stanovenou úroveň"); Level = 0; } else { Print(„Cena dosud stanovenou úroveň nepřekročila“); } První řádek, tedy if (Bid > Level) představuje hlavičku. V závorce za identifikátorem if je zadána podmínka, která bude operátorem nejprve vyhodnocena. V tomto případě musí rozhodnout, zda platí, že aktuální cena Bid je vyšší než hodnota proměnné Level. Pokud tomu tak je a podmínka je tedy splněna, vykoná se blok operací ve složené závorce pod hlavičkou. V našem případě se exekuuje funkce Alert, která traderovi zobrazí prostřednictvím dialogu upozornění s textem umístěným v závorce a hodnota proměnné Level se nastaví na nulu. Obě operace musí končit středníkem. Pokud podmínka splněna není, tedy pokud je cena Bid nižší nebo rovna hodnotě proměnné Level, vykoná se blok operací ve složené závorce za identifikátorem else. V tomto případě vykoná pouze funkci Print, která zapíše záznam do programového logu (záložka Deník v terminálu). Záznam odpovídá zadanému parametru funkce, který se vepisuje do závorky. Tvar operátoru if-else může ale část else úplně postrádat. if (Bid > Level) { Alert("Cena překročila stanovenou úroveň"); Level = 0; } V tomto případě by byl postup stejný v případě splnění podmínky, ale odlišný v případě, že by podmínka splněna nebyla. Z důvodu absence části operátoru else není dáno, co má program v takovém případě dělat. Nebude proto dělat nic, respektive bude pokračovat v exekuci dalšího kódu, ať už následuje za operátorem cokoliv. Nakonec existuje ještě jednodušší tvar: if (Bid > Level)  Alert("Cena překročila stanovenou úroveň"); Exekuce je zde taková, že pokud je podmínka v hlavičce splněna, vykoná se funkce Alert. Všimněme si ale, že zde chybí složené závorky. Nyní už je vám asi jasný jejich význam. Podle složených závorek operátor pozná, co všechno má exekuovat. Pokud složené závorky nejsou, exekuuje se pouze první operace, další jsou z hlediska exekuce již mimo tělo operátoru a vyhodnocení podmínky na ně nemá vliv.

Cyklický operátor while

While je jednodušším ze dvou cyklických operátorů. Jeho účelem je vykonávat blok operátorů stále dokola, dokud je splněna zadaná podmínka. Obecně je jeho tvar takovýto: while („podmínka“) { blok operátorů k vyplnění; } Podmínka je opět zadána v závorce v hlavičce. Následuje blok operátorů umístěný ve složených závorkách. Ten se exekuuje neustále dokola, dokud je podmínka v hlavičce splněna. Jedna exekuce cyklu se nazývá iterace. Kontrola podmínky je provedena na konci každé iterace. Pokud je splněna, exekuuje se znovu, pokud už není, blok se přeskočí a pokračuje se v následujícím kódu programu. Příklad je uveden pod operátorem for, kterým lze operátor while ve většině případů nahradit.

Cyklický operátor for

for (Výraz1; „podmínka“; Výraz2) { blok operátorů k vyplnění; } V hlavičce tentokrát najdeme tři výrazy oddělené středníkem. Při exekuci tohoto operátoru nejdříve program vykoná Výraz 1, tedy vlevo v hlavičce. Poté vykoná blok operátorů ve složených závorkách, přičemž na konci každé iterace vykoná Výraz2, který je v hlavičce úplně vpravo. Iterace vykonává neustále dokola, dokud je splněna podmínka, která je umístěna uprostřed hlavičky. Na následujícím příkladě si ukážeme, že operátory while a for jsou vzájemně nahraditelné. Srovnejte tuto část kódu s operátorem while:

…a tuto část s operátorem for:

Když si pozorně projdete tyto dva bloky kódu, měli byste dojít k závěru, že obě části kódu se exekuují s naprosto stejným výsledkem. Obě části začnou deklarací proměnné x a přiřazení hodnoty 1. Pak se vykonává blok operací neustále dokola, přičemž na konci každé iterace je hodnota x zvýšena o 1. Iteruje se do té doby, dokud platí, že x je menší než hodnota proměnné PocetUsecek. Také byste měli dojít k tomu, že hodnota proměnné Soucet bude na konci rovna průměru high a low všech svíček grafu, což je cílem obou bloků kódu. Na příkladech mimo jiné vidíme, proč se v praxi častěji používá operátor for. Tváří se sice složitěji, ale je praktičtější a úspornější na místo. To byly nejvýznamnější operátory, bez kterých se žádný programátor neobejde. V příští kapitole si ukážeme zbývající operátory, aby byl jejich výčet kompletní, a nakousneme poslední zásadní kapitolu, kterou je třeba probrat, než se pustíme do praktického programování - funkce.

Operace

Co je to operace? Jaké jsou druhy operací? Jaké operace můžeme v kódu používat? Jak to, že operace X = X + 2 je v pořádku? Tato kapitola by měla být pro většinu z vás spíše odpočinková. Je potřeba si ujasnit, jaké operace můžeme v MQL kódu používat a jakým způsobem. Řekneme si taky o některých specificích, která nejsou úplně intuitivní, pokud s programováním začínáte. Tvůrci jazyka MQL dělí operace do sedmi kategorií, my si prozatím vystačíme se čtyřmi hlavními. Jsou to operace, které povětšinou znáte ze základní a střední školy. Je však potřeba ujasnit některé jejich vlastnosti.

Aritmetické

Hned první kategorie operací je všem dobře známá. Při provádění výpočtů můžeme používat aritmetických symbolů tak, jak to známe ze školy, tedy + pro sčítání, - pro odčítání, * pro násobení a / pro dělení. Jsou tu jen tři specifika, která ze školy neznáme. Symbol % jakožto symbol operace vrací zbytek po dělení a dále se hodně využívají symboly ++ a --, které sníží, respektive zvýší proměnnou o 1. Pro jistotu ještě malá tabulka:

Symbol Operace Příklad Analogie
% Zbytek po dělení minuta = čas % 60
++ Zvýšení hodnoty proměnné o 1 y++ y = y + 1
-- Snížení hodnoty proměnné o 1 y-- y = y - 1

Přiřazovací

Přiřazovacími operacemi měníme hodnotu proměnné. Děje se to klasicky prostřednictvím symbolu =. Nyní upozorním na jeden neintuitivní rys programování, se kterým se začátečníci občas nemohou smířit. Výraz A = B + C v kódu neoznačuje rovnici. Pokud by tomu tak bylo, pak by výraz X = X + 2 byl nesmyslný. To však není. Jelikož symbol = označuje přiřazovací operaci, nikoli symbol rovnosti, program si výraz vyloží jako „proměnné X přiřaď výraz X+2“. Pokud tedy X před vykonáním této operace je 2, po ní bude 4. Pokud bude operace vykonána znovu, pak nové X bude 6. A tak dále. Toto pravidlo se uplatňuje v následující tabulce přiřazovacích operací.

Symbol Operace Příklad Analogie
= Přiřazení hodnoty proměnné x proměnné y у = x
+= Zvýšení hodnoty y o x у += x y = y + x
-= Snížení hodnoty y o x y -= x y = y - x
*= Znásobení hodnoty y x-krát y *= x y = y * x
/= Dělení hodnoty y hodnotou proměnné x y /= x y = y / x
%= Zbytek po dělení y proměnnou x y %= x y = y % x

Relační

Relační neboli porovnávací operace využíváme k porovnání hodnot dvou a více proměnných. Využití najdou především v hlavičkách složených operátorů, kterým se budeme věnovat od příštího dílu. Většinu symbolů opět známe ze školních lavic, jiné jsou ryze z programátorského světa. V tabulce vidíte, že symbol rovnosti je ==, místo očekávaného =, které symbolizuje operaci přiřazení. Pozor na záměnu těchto dvou symbolů, jedná se o velmi častou chybu, která se ale ne vždy snadno odhaluje.

Symbol Operace Příklad
== Pravda, pokud x je rovno y x == y
!= Pravda, pokud x není rovno y x != y
< Pravda, pokud je x menší než y x < y
> Pravda, pokud je x větší než y x > y
<= Pravda, pokud je x menší nebo rovno y x <= y
>= Pravda, pokud je x větší nebo rovno y x >= y

Logické (Boolean)

O logických neboli booleanovských operacích jste pravděpodobně slyšeli na škole střední. Opět se použijí hlavně u složených operátorů. Jsou to tyto tři:

Symbol Operace Příklad Popis
! NOT (negace) х!=0 Pravda, pokud x není rovno 0
|| OR (disjunkce) x < 5 || x > 7 Pravda, pokud platí, že x < 5 NEBO x > 7
&& AND (konjunkce) x == 3 && y < 5 Pravda , pokud platí, že x je rovno 3 A ZÁROVEŇ y < 5

Výčet základních operací je tímto pro nás hotový. Ještě dodám, že operací je rovněž volání funkcí, tu si však rozebereme, až budeme mít za sebou funkce.

Operátory II

V předchozí kapitole jsme si ukázali základní operátory, bez kterých se neobejde téměř žádný program. Šlo o podmínkový operátor if-else a dva cyklické operátory while a for. V této kapitole se zaměříme na zbývající, můžeme říci méně důležité operátory a uděláme první úvod do funkcí.

Operátory break a continue

Operátory break a continue se používají výhradně uvnitř cyklických operátorů. Lze je tedy použít pouze ve spojení se složenými operátory while, for a switch. Oba operátory patří mezi jednoduché, stačí tedy jen napsat jejich název a za něj umístit středník. Break má za úkol „násilně“ ukončit činnost cyklického operátoru. Využije se například v případě, že je za nějaké podmínky vhodné cyklický operátor ukončit dříve, než se podmínka jeho činnosti stane neplatná. Například v této části kódu je exekuce operátoru for ukončena v případě, že je v historii pozic nalezena pozice, která odpovídá zadaným kritériím. Bez operátoru break by cyklus pokračoval v procházení seznamu historických pozic až do konce, což ale není potřeba. Takto se činnost operátoru ukončí ihned po nalezení hledané pozice, což je mnohem efektivnější.

Operátor continue pro změnu ukončuje pouze exekuci jedné iterace cyklu. Použijeme ho v případě, kdy chceme za nějaké podmínky vypustit exekuci zbývající části cyklu a přejít rovnou k další iteraci. Můžeme opět uvést část kódu, kde se tento operátor vyskytuje.

Tento řádek říká, že pokud je pátek a zároveň více než 23 hodin nebo sobota nebo neděle a zároveň méně než 22 hodin, tak program napíše do horní levé části grafu "Neobchoduje se, neaktualizuje se" a ukončí iteraci cyklu a přejde na další iteraci. Tento řádek by měl smysl například ve speciální funkci OnTimer(), která se vykonává periodicky. Pokud by byly splněny podmínky, tedy pokud by čas byl mimo obchodní hodiny, nemělo by smysl v iteraci pokračovat a přešlo by se k další. Takto by program šetřil mnoho zdrojů do doby, než by se obchodovat začalo.

Operátor return

Return je zcela zásadní jednoduchý operátor u funkcí. Většina funkcí vrací určitou hodnotu. Kterou hodnotu má funkce vrátit určuje právě operátor return. Ukázku uvidíme hned v následujícím odstavci a především pak v následujícím díle.

Operátor switch

Operátor switch má za úkol zjednodušit práci tam, kde by rozhodovací mechanismus vyžadoval příliš velké množství podmínkových operátorů. V hlavičce napíšeme proměnnou, jejíž hodnota ovlivní další činnost operátoru. Následně rozepíšeme, co má program udělat v případech, že bude hodnota proměnné rovna nějaké hodnotě.

Tento příklad operátoru switch, který je součástí uživatelské funkce, má za úkol rozhodnout, jakou hodnotu funkce vrátí v závislosti na hodnotě proměnné "typ". Z praktického hlediska se jedná o operátor, který pojmenuje typ pozice. Typy pozice jsou totiž v MQL zakódovány indexy od 0 do 7. Který typ pozice index označuje, je vidět v našem příkladu. Switch funguje tak, že zjistí hodnotu proměnné "typ" a poté vyhledá tuto hodnotu za výrazem "case". Když ji najde, vykoná sadu operátorů za dvojtečkou ve stejném řádku (v tomto případě vrátí název typu pozice). Pokud ji nenajde, vezme sadu operátorů za výrazem "default". Každý řádek je ukončen operátorem break, protože je jasné, že jakmile switch našel požadovaný řádek, není třeba hledat dál.

Funkce a volání na funkci

V předchozích kapitolách jsme si již představili základní typy funkcí, které v programech využíváme. Jsou to funkce speciální, které nemohou chybět v žádném programu, dále funkce standardní neboli předdefinované a funkce uživatelské. Nejprve se budeme věnovat obecnému úvodu do problematiky funkcí, dále se podíváme na funkce standardní, blíže si rozebereme obchodní funkce a nakonec ještě uživatelské.

Funkce

Funkce obecně je část kódu, která má za úkol předepsaným způsobem vyřešit nějaký problém nebo zadání a obvykle vrátit určitou hodnotu. Obecný tvar funkce vypadá následovně: int Moje_funkce(double par1, string par2, int par3) { blok určený k exekuci – tělo funkce return(vracena_hodnota); } První řádek se označuje jako hlavička a v ní je definován datový typ funkce, který odpovídá datovému typu vrácené hodnoty (v tomto případě int, tedy celé číslo), následně název funkce a v závorce seznam parametrů s příslušnými datovými typy. Následují složené závorky a v nich uložené tělo funkce, tedy její samotná podstata – skupina operátorů, operací, dalších funkcí atd. V těle musí být umístěn operátor return a za ním v závorce hodnota, kterou má funkce vrátit (nejedná-li se o funkci typu void). Operátor return slouží zároveň i jako ukončení funkce, po kterém kontrola funkci opouští a vrací se do původního kódu. Většina funkcí pracuje se vstupy, takzvanými parametry, které jí musíme zadat. Parametry se zadávají za název funkce do závorky, oddělují se čárkou. Parametr umožňuje převod nějaké hodnoty proměnné z okolního kódu dovnitř funkce, aby s ní mohla pracovat. Například takto vypadá hlavička funkce se dvěma parametry. double Podil(double zisk_dny, int ztrat_dny) Jsou ale i funkce, které žádné parametry pro svou práci nepotřebují. V závorce prostě nebudou mít nic, ale samotná závorka tam být musí, aby program poznal, že jde o hlavičku funkce a nikoli o proměnnou. Taková funkce může vypadat následovně:

Jde o funkci, která v historii vyhledá pozice s typem 5 a více, což jsou deposity a výběry. Jejich hodnoty sečte, uloží do proměnné Kapitál a tuto vrátí. Na funkci lze nahlížet třeba jako na tiskárnu. Potřebuje vstupy - parametry (tonery, papír, elektřinu) a výstupem bude vytištěný dokument. U funkce je výstupem nějaká získaná hodnota - číslo, text, kterýkoliv z přípustných datových typů, které jsme rozebírali v pátém díle. Datový typ, který funkce vrací, určuje datový typ celé funkce. Obojí musí být v souladu. Stejně jako existují funkce, které nepotřebují vstupy, existují i funkce, které nevracejí žádnou hodnotu. Říká se jim funkce typu „void“. Jejich účelem potom není vrátit hodnotu, ale exekuce samotná. Jde například o hojně využívanou standardní funkci Alert(), která v terminálu zobrazí upozornění se zadaným textem. Jako taková ale nevrací do kódu žádnou hodnotu. Mezi funkce typu void patří mimochodem i speciální funkce OnDeinit() nebo třeba OnTick(). I tak ale funkce typu void mohou obsahovat operátor return. Ten se hodí třeba v případě, že je vhodné za určité podmínky funkci ukončit dříve, tedy když za určité podmínky není potřeba funkci dále exekuovat (je to vlastně analogie s operátorem break v cyklu). Jen za tímto operátorem bude chybět závorka s vracenou hodnotou. Opět neuškodí příklad s částí funkce:

Toto je ukázka z příkladového obchodního systému Moving Average, který je součástí každého MetaEditoru. Řádky 138 a 139 jednoduše říkají, že pokud je na grafu méně než 100 svíček nebo je zakázáno automatické obchodování, funkce OnTick má být ukončena (a kontrola tedy má být předána funkci OnDeinit a program ukončen). Funkce obsahuje každý program. Pokud ne jiné, tak speciální určitě. O speciálních funkcích jakožto tahounech veškeré exekuce jsme již napsali mnoho. Dodám jen, že z výše uvedeného by vám mělo vyplynout, že celý program je vlastně jedna velká funkce vzhledem k tomu, že veškerá část, která se exekuuje (tedy vše kromě hlavičky programu), se odehrává uvnitř speciálních funkcí.

Volání na funkci (function call)

Aby program věděl, kdy má funkci exekuovat, je potřeba se ještě seznámit s jejich spouštěči. Jsou to operátory, kterým se říká Function call, tedy volání funkce. Jedinými funkcemi, na které se nevolá, jsou funkce speciální. Respektive, volá na ně sám terminál podle určitého klíče. Například při spuštění programu se zavolá speciální funkce OnInit(), při příchozím ticku OnTick(), při spuštění skriptu OnStart() a tak dále. V předchozí kapitole jsme si ukázali, jak vypadá samotná funkce, nyní si ukážeme tvar operátoru volání funkce. Ten je následující: Moje_funkce (par1, par2, par3); Operátor sestává z názvu volané funkce a hodnot předávaných parametrů. Ukončen je samozřejmě jako každý jiný operátor středníkem. Počet a datové typy parametrů volání funkce a dané funkce musí souhlasit, jinak kompilátor oznámí chybu. Krásně to ilustruje následující obrázek, který jsem si vypůjčil z book.mql4.com. Z něj je také vidět, že předávaný parametr může být jakýkoliv datový typ a může to být konstanta, výraz nebo proměnná.

Zároveň vidíme, že jakmile kontrola zaregistruje volání funkce, exekuce se přesune k tělu této funkce. Ta může být v případě uživatelské funkce umístěna kdekoliv v programu, samozřejmě mimo těla jiných funkcí. Ovšem v případě, že jde o funkci standardní neboli předdefinovanou, tak funkce samotná v programu vůbec není potřeba, stačí na ni jen zavolat. Můžeme si to představit tak, že kódy těchto funkcí jsou již hotové a zabudované přímo v Metatraderu.

Standardní funkce

Standardní funkce jsou z hlediska programátora nejpohodlnější. Není potřeba definovat ani jejich název, ani množství a typ parametrů, ani datový typ. To vše je již definováno tvůrci jazyka. Stačí prostě jen v programu na tuto funkci zavolat, tedy napsat její název, do závorky zadat správné hodnoty parametrů (pokud jsou vyžadovány) a program už bude vědět, co má udělat. Při psaní kódu se předdefinované funkce barevně odliší. Funkcí tohoto typu je kolem 250. Jak se ale dozvíme, které parametry funkce chce, v jakém pořadí a jakého jsou typu? A jak se vůbec dozvíme, co která funkce dělá? Činnost funkce obvykle plyne z názvu, počet parametrů a jejich typ potom vidíme v tooltipu při psaní kódu. Konkrétně se objeví po napsání otevírací závorky funkce a aktuální parametr se zvýrazní tučně:

Přesto je obvykle o něco pohodlnější, zvlášť pokud funkci ještě neznáme, vyhledat si svou funkci na stránkách docs.mql4.com. V levém menu si můžeme vybrat kategorii funkce a získáme všechny dostupné funkce dané kategorie. Po rozkliknutí funkce pak najdeme veškeré potřebné informace k jejímu použití. Například v kategorii Technical Indicators najdeme všechny funkce, které vracejí hodnoty technických indikátorů, což jsou mimochodem velice užitečné funkce pro obchodní systémy, které využívají nějakého indikátoru. Podívejme se, co zjistíme, když otevřeme například funkci iMA. Pochopitelně v této fázi už nebude stačit čeština, ale je potřeba základní znalost angličtiny, ruštiny nebo čínštiny. Možná tvůrci stránek jazykovou nabídku do budoucna rozšíří, ale s češtinou bych moc nepočítal navzdory tomu, že MetaEditor byl nedávno přeložen i do češtiny.

První odstavec stručně a jasně popisuje, co má funkce za úkol. V tomto případě je úkolem funkce vypočítat ze zadaných parametrů klouzavý průměr a vrátit jeho hodnotu. Následuje tvar funkce, ze kterého poznáme datový typ funkce a pořadí a datové typy parametrů. Pro přehlednost a kvůli vysvětlujícím komentářům zde má každý parametr jeden řádek, v praxi je lze samozřejmě psát za sebe do jednoho řádku. Další blok je výčtem jednotlivých parametrů s popisem a většinou i s hodnotami, které za tento parametr lze dosadit. Například o prvním parametru „symbol“ typu string, tedy textový řetězec, se dozvíme, že označuje název symbolu, z jehož dat je hodnota indikátoru vypočítána. Pokud dosadíme NULL, vezme se symbol grafu, na kterém strategie běží. Nebo za parametr „ma_method“ typu celé číslo musíme dosadit kód metody, na základě kterého se průměr bude počítat. Připojen je i odkaz na tabulku, kde je rozepsáno, který kód přísluší které metodě. Velice důležitý je řádek Returned value, ve kterém se dozvíme, jakou hodnotu nám funkce vrátí. V tomto případě vrátí numerickou hodnotu klouzavého průměru typu double, tedy ve formě desetinného čísla. V posledním bloku je potom příklad použití dané funkce. Na závěr si ještě uveďme přehled často používaných standardních funkcí se stručným popisem jejich použití. AccountInfoDouble() – slouží pro vracení různých informací o účtu, které mají formu desetinného čísla jako equity, profit apod. Typ informace zadáváme do parametru. Podobné jsou ještě AccountInfoInteger() a AccountInfoString(), které vracejí informace celočíselné a textové. Například funkce AccountInfoString(ACCOUNT_CURRENCY) vrátí zkratku měny účtu. Alert() – zobrazí uživateli klasické okno s upozorněním a přehraje zvukový tón. Parametry funkce jsou argumenty zobrazené v upozornění. Text musí být uveden v uvozovkách. Např. Alert(„Otevřen “,SmerPozice,“ za “,OteviraciCena). Takto postavená funkce upozorní tradera na směr a otevírací cenu nové pozice, pakliže jsou samozřejmě uloženy příslušné hodnoty v proměnných SmerPozice a OteviraciCena.

Comment() – zobrazí nápis v levé horní části grafu. Pro parametry platí totéž co u funkce Alert().

DoubleToStr() – zaokrouhlí hodnotu typu double na určitý počet desetinných míst. Prvním parametrem je hodnota, druhým výsledný počet desetinných míst. MarketInfo() - podobně jako AccountInfo() tato funkce umožňuje vyvolat informace o instrumentech, které jsou zobrazeny v panelu Trh, jako například jejich spread, ceny, minimální velikost pozice, minimální vzdálenost stop příkazů apod. ObjectCreate() – slouží pro tvorbu různých grafických prvků do grafu (tvary, texty, šipky atd.). Parametry určují typ předmětu, jeho umístění a další vlastnosti. Print() – zapíše záznam do programového logu (záložka Deník v terminálu). Parametr viz funkce Alert() SendMail() – Odešle email na adresu nastavenou v terminálu (Nástroje -> Možnosti -> Email). Parametry jsou předmět a text emailu. Ideální třeba na upozornění na určitou situaci na trhu. StringConcatenate() – Ze zadaných argumentů vytvoří textový řetězec. Využití najde třeba pro sestavení řetězce k použití ve funkci SendMail(). TimeCurrent() – vrátí aktuální čas serveru brokera ve formátu datetime. Žádný parametr nepotřebuje. Obdobou je funkce TimeLocal(), která vrátí čas počítače. Záměrně jsem vynechal funkce obchodní, kterým příště věnuji samostatný díl. Předpokládám, že většina z vás se tento jazyk učí pro programování obchodních systémů, proto se na tyto funkce podíváme podrobně.

Obchodní funkce

Většina z nás se jazyk MQL učí za účelem programování automatických obchodních systémů. Ty se samozřejmě neobejdou bez funkcí, které zajišťují otevírání, uzavírání a správu obchodních pozic. Tyto funkce se souhrnně nazývají obchodní funkce, v dokumentaci docs.mql4.com je najdeme ve skupině Trade functions. Ty nejdůležitější z nich si v této kapitole rozebereme.

OrderSend()

Funkce OrderSend() je jedinou funkcí, která dokáže otevřít pozici nebo zadat limitní pokyn. V případě úspěšného otevření pozice vrátí číslo tiketu otevřené pozice, jinak -1. Z tohoto důvodu je vhodné funkci umístit např. do podmínkového operátoru, který v případě výsledku funkce -1 zjistí pomocí funkce GetLastError(), kde se stala chyba. Funkce má následující tvar:

Vidíme poměrně velké množství parametrů, pojďme si je popsat. symbol – zkratka symbolu, na kterém se má pozice otevřít (např. „EURUSD“, záleží na brokerovi, jak instrument značí ve své platformě). Často se za parametr vkládá funkce Symbol(), která vrátí název symbolu grafu, na kterém strategie běží. cmd – Označuje typ příkazu podle následující tabulky. Zadat můžeme jak hodnotu ID, tak i ve formě čísla.

ID Hodnota Popis
OP_BUY 0 Nákup Market
OP_SELL 1 Prodej Market
OP_BUYLIMIT 2 Limitní příkaz Buy Limit
OP_SELLLIMIT 3 Limitní příkaz Sell Limit
OP_BUYSTOP 4 Limitní příkaz Buy Stop
OP_SELLSTOP 5 Limitní příkaz Sell Stop

volume – označuje velikost pozice v lotech. Může to být samozřejmě přímo napsané číslo, vzorec, nebo uživatelská funkce, která má za úkol podle různých parametrů trhu a účtu vypočítat optimální velikost. Setkáme se s ní například v ukázkové strategii Moving Average. Hodnota musí být povolena brokerem, jinak se pozice neotevře, funkce vrátí -1 a GetLastError() vrátí chybu 131 (ERR_INVALID_TRADE_VOLUME). price – ano, je potřeba zadat i otevírací cenu, a to buď Ask nebo Bid. Program totiž neví, která z těchto cen je správná pro otevření dané pozice. Pro úplnost dodávám, že nakupujeme za cenu Bid a prodáváme za Ask. Cena Bid nebo Ask platí pro instrument grafu, na kterém běží strategie. Pokud chceme otevřít pozici jiného instrumentu (a máme tedy v parametru symbol jeho název), pak musíme cenu zjistit pomocí funkce MarketInfo(symbol, Bid/Ask). Pokud cenu zadáme špatně, funkce opět skončí chybou. slippage – udává maximální odchylku v pipech od požadované ceny, tedy maximální vámi akceptovatelný skluz. Pokud bude vyšší, pozice se neotevře. stoploss – cena, na kterou bude umístěn příkaz Stoploss. Obvykle se v praxi zadává jako vzdálenost od vstupní ceny, např. „Ask + 10*Point“ v případě krátké pozice. Často se také jedná o výsledek komplikovanější funkce, jak úroveň tohoto příkazu spočítat. Je potřeba dát pozor na to, aby stoploss byl pod cenou v případě longové pozice a nad cenou v případě shortu. Pokud bychom parametr zadali špatně, nemusíte se obávat, že by se pozice otevřela bez stoplossu. Neotevře se vůbec, skončí chybou. takeprofit - cena, na kterou bude umístěn příkaz Takeprofit. Opět se obvykle zadává jako vzdálenost od vstupní ceny, např. „Bid + TakeProfit*Point“ v případě dlouhé pozice, kde proměnná TakeProfit může být třeba uživatelsky nastavená. comment – komentář k dané pozici ve formě řetězce, musí být tedy v uvozovkách. magic – „magické číslo“, které lze použít například pro odlišení obchodů otevřených konkrétní strategií. Výchozí hodnotou je nula, tedy žádné magické číslo. expiration – datum a čas expirace limitního příkazu. Nejčastěji se zadává jako rozdíl oproti času zadání příkazu TimeCurrent() nebo TimeLocal(), tedy např. „TimeCurrent()+3600“ pro expiraci za hodinu. arrow_color – sem zadáváme případnou barvu šipky, kterou chceme zobrazit na grafu v době otevření pozice. Klasicky bychom mohli zadat clrRed pro shorty a clrGreen pro longy. Pokud necháme pole prázdné nebo zadáme nulu, žádná šipka nebude zobrazena. Pro úplnost uvedu dva příklady, v jakém tvaru může funkce být:

Tato funkce otevírá pouze longy na páru GBP/JPY a SL a TP umisťuje ve vzdálenosti 15 pips. Poslední 4 parametry úplně chybí, takže se přijímají jejich výchozí hodnoty. Konkrétně tedy tato funkce bude bez komentáře, magického čísla i bez šipek v grafu. Teď se podívejme na část kódu v kontextu i s ověřováním úspěchu a zobrazení případné chybové hlášky:

Vidíme, že funkce OrderSend() zde otevírá shorty na instrumentu dle grafu, na kterém běží. Pro velikost pozice se zavolá funkce Objem(). Úrovně stoplossu i profit targetu jsou již předem uloženy v proměnných. Dále vidíme, že výsledek funkce se šikovně uloží do proměnné ticket a potom je analyzován. Pokud je vyšší než 0, znamená to, že pozice byla úspěšně otevřena a pomocí funkce Print() je o tom zapsán záznam do logu. Pokud je ale ticket menší než 0, znamená to, že se někde stala chyba. Potom se do logu vytiskne hlášení o chybě a číslo této chyby získané funkcí GetLastError(). Kódy chyb lze analyzovat na stránce https://book.mql4.com/appendix/errors

OrderSelect()

Tato funkce je velmi důležitá pro načtení konkrétní pozice (otevřené nebo uzavřené) a další práci s ní (uzavírání, změna, získání informací atd.). Většinou bývá umístěna v cyklu, který pozice postupně projíždí. Vrací true v případě úspěšného výběru a false v případě neúspěchu. Parametrů máme již méně:

index – tento parametr závisí na druhém paramatru. Buďto se bude jednat o pořadové číslo nebo o konkrétní číslo tiketu. select – udává, na základě čeho se má pozice vybrat. Jsou dvě možnosti: SELECT_BY_POS – vybrat dle indexu v tabulce pozic, de facto podle pořadového čísla; SELECT_BY_TICKET – vybrat dle čísla tiketu. pool – udává, ve které tabulce se má hledat. Má smysl pouze, pokud předchozí parametr je SELECT_BY_POS. Možné jsou opět dvě hodnoty: MODE_TRADES (výchozí) – vybrat z tabulky Obchod, tj. z otevřených pozic a čekajících limitních pokynů; MODE_HISTORY – vybrat z tabulky Historie účtu, tj. ze seznamu uzavřených pozic a zrušených pokynů. Část kódu obsahující tuto funkci pak může vypadat třeba takto:

Cyklus projíždí všechny pozice v tabulce Historie účtu a funkce OrderSelect vždy načte jednu z nich. Všechny další obchodní funkce uvnitř podmínkového operátoru se potom budou vztahovat právě k vybrané pozici. Může jít o funkce jako OrderProfit() vracející profit této pozice, OrderOpenTime() vracející datum a čas otevření pozice apod.

OrderClose()

Jakmile umíme pozici otevřít a načíst, bude určitě dobré se naučit pozice také uzavírat (pokud nám nestačí zavírání pomocí stoplossů a profit targetů). Bude to jednoduché:

S parametry se zachází naprosto stejně, jako v případě OrderSend(). Jak vidíte, abyste uzavřeli konkrétní pozici, musíte získat její ticket. Pokud váš systém umožňuje mít otevřenou vždy jen jednu pozici, lze použít proměnnou, kam se tiket uloží při jejím otevření. Pokud tomu ale tak není, nezbývá než pozici nejdříve vybrat pomocí funkce OrderSelect() a potom můžeme do parametru ticket umístit funkci OrderTicket(). Na závěr ještě krátce zmíníme některé další důležité obchodní funkce. OrderCloseBy() – uzavře pozici a otevře novou pozici v opačném směru, OrderDelete() – zruší otevřený limitní příkaz, OrderModify() – změní příslušnou otevřenou pozici nebo limitní příkaz (např. posun stoplossu, změna expirace apod.), OrdersHistoryTotal() – vrací počet záznamů v tabulce Historie účtu, OrdersTotal() – vrací počet otevřených pozic a aktivních čekajících příkazů. Ostatní funkce, které nalezneme v příslušné skupině, již vracejí informace o konkrétní načtené pozici. Nezapomeňte, že pro načtení do mezipaměti lze použít jedině funkci OrderSelect(). V dalším díle zjistíme, co máme dělat v případě, že funkce, kterou bychom potřebovali, není mezi standardními předdefinovanými funkcemi. V takovém případě si ji musíme vytvořit sami a příště si povíme, jaká u toho musíme respektovat pravidla.

Uživatelské funkce

Již v dřívějších kapitolách jsme uvedli, že uživatelské funkce jsou takové funkce, které musíme v rámci programu nadefinovat. Tím se liší od funkcí standardních, které definovat nemusíme, stačí na ně jen zavolat pomocí operátoru volání funkce. Z tohoto pohledu lze za uživatelské funkce považovat také funkce speciální, jelikož i ty definujeme sami, kromě jejich typu a názvu. Jejich jedinou odlišností je, že je nemusíme volat, volá na ně sám terminál MT4. Teoreticky lze speciální funkci zavolat i ručně z programu, ale sami tvůrci jazyka to nedoporučují. Vše shrnuje následující tabulka:

Typ funkce Předpis funkce v programu Volání funkce
Speciální Ano Ne
Standardní Ne Ano
Uživatelská Ano Ano

Uživatelskou funkci si tedy musíme předepsat a pro její exekuci jí musíme také zavolat. Jak vypadá operátor volání funkce i předpis funkce jsme se dozvěděli již v jedenáctém díle, tedy úvodním díle o funkcích. Pro připomenutí ale níže přidáme obrázek, na kterém je tvar obou záležitostí vidět. Stejně tak je na něm vidět, jak probíhá transfer parametrů mezi operátorem volání funkce a hlavičkou předpisu funkce. Plyne z něj, že proměnné uvnitř naší funkce jsou naprosto nezávislé na všech proměnných mimo funkci (s výjimkou proměnných globálních – viz šestý díl o proměnných). Všechny proměnné, které použijeme ve funkci, musíme nadeklarovat. Přesto ale můžeme použít hodnoty proměnných nebo výrazů mimo naší funkci, a to právě s pomocí parametrů. Z obrázku by mělo být vše jasné:

Ještě také pro jistotu připomenu, že předpis uživatelské funkce musí být umístěn mimo těla ostatních funkcí. Využití uživatelské funkce si ukážeme na jednoduchém prográmku, jehož úkolem je ze zadaných stran odvěsen vypočítat délku přepony pravoúhlého trojúhelníku dle známé Pythagorovy věty c^2 = a^2 + b^2, kde c je délkou přepony, a s b jsou délkami odvěsen. Možností, jak skript sestavit, je mnoho. Začneme tou bez využití uživatelské funkce:

Z komentářů v kódu by měl být postup jasný. Pro výpočet odmocniny slouží v MQL funkce MathSqrt(), výsledek je uživateli oznámen funkcí Alert(). Nyní si ukážeme, jak je možné celý program zkonstruovat s využitím uživatelské funkce: Pro výpočet přepony C je zavolána funkce Pythagoras, která za využití parametrů, tedy hodnot odvěsen, přeponu vypočítá a vrátí výsledek. Ten je poté prezentován uživateli. Princip nezávislosti proměnných jsme znázornili tak, že ve funkci Start() jsou názvy stran označeny velkými písmeny, kdežto uvnitř funkce Pythagoras() písmeny malými. Zvoleny by však mohly být jakékoliv jiné názvy, jak si následně ukážeme. Na programování je úžasné to, že kód, jehož exekuce přinese stejný výsledek, může více programátorů sestavit diametrálně odlišně. Pojďme se podívat na další 2 zajímavé možnosti, jak může vypadat tělo funkce Pythagoras.

V této verzi jednak zdůrazňujeme nezávislost proměnných na vnějších proměnných použitím úplně jiných názvů. Především je ale několikrát znovu použita proměnná alfa jako uchovatel hodnoty, čímž se snažíme ukázat, že teoreticky není potřeba dalších proměnných, které by uchovávaly hodnotu c^2 a c. Záleží jen na hodnotě, kterou funkce vrátí a je jedno, která proměnná jí uchová. Hodnota proměnné alfa se během funkce dvakrát změní, na konci má však právě hodnotu délky přepony. Na celkovém výsledku to tedy vůbec nic nemění, ale je to obvykle méně přehledné a méně „pěkné“, záleží na vkusu programátora.

Takto vypadá nejúspornější a nejelegantnější varianta funkce, kdy celý výpočet je umístěn v operátoru return. Ne vždy je to ale možné a ne pro každého je tato varianta přehledná a intuitivní, proto se často používají rozepsané varianty. Když se zbavíme uživatelské funkce a použijeme toto řešení, zjistíme, že celý problém se dá vyřešit pouhými několika řádky. Následující ukázka představuje celý kód. Kód je vylepšený ještě o to, že délky odvěsen zadává sám uživatel.

Jednoduchý program

Z předchozích kapitol již známe všechny základní prvky, které programovací jazyk MQL používá pro tvorbu obchodních systémů a dalších programů do platformy Metatrader. Víme, jak deklarovat a inicializovat proměnné. Známe použitelné operace. Máme jasno ve struktuře programu a náležitostech pro různé typy programů. Víme, k čemu slouží které operátory. V neposlední řadě známe základní funkce, umíme si vytvořit funkci vlastní anebo víme, kde si najít standardní funkci, kterou potřebujeme. Zbývá již jen trénovat, zkoušet, překonávat překážky, učit se a vylepšovat své programy. Poslední kapitoly proto budou ryze praktické. Nejprve si ukážeme stavbu jednoduchého programu, který bude mít za úkol nás informovat o průměrném rozpětí posledních X period. Nejdříve takový program napíšeme a pak vymyslíme, jak bychom ho mohli vylepšit. První zadání zní následovně:

  1. Sestavte program, který jednorázově vypočítá průměrné rozpětí posledních 20 kompletních svíček grafu a oznámí výsledek.

Protože se má jednat o jednorázovou akci, víme, že potřebujeme vytvořit skript. Když nám MetaEditor vytvoří předlohu pro skript, je jasné, že veškerý kód umístíme do speciální funkce OnStart(). Program bude vhodné rozdělit na tři části. V první části nadeklarujeme, případně inicializujeme proměnné. Ve druhé části sečteme rozpětí oněch 20 posledních svíček. K tomu nám dopomůže cyklický operátor for a využijeme proměnné uzpůsobené na práci s časovými řadami. V poslední části výsledný součet vydělíme 20, čímž získáme průměrné rozpětí a nakonec o výsledku informujeme uživatele skriptu. S částí kódu jsme se již setkali v jednom z předchozích dílů. Podívejme se, jak by výsledný kód mohl vypadat. Každý řádek je vysvětlen pomocí komentáře.

  1. Přetvořte program tak, aby informaci o rozpětí neustále aktualizoval a zobrazoval v grafu, aniž by bylo třeba čekat na první tick.

Nyní se již nebude jednat o jednorázovou akci, ale o pravidelně aktualizovanou informaci. Tím pádem musíme opustit skript a vytvořit strategii, která se exekuuje, v tomto případě přepočítá, s každým příchozím tickem (jindy se průměr z principu změnit nemůže). Tento požadavek splňuje program typu indikátor nebo automatický obchodní systém (strategie). O indikátor se nejedná, zvolíme tedy druhou variantu. Vytvoříme nový program, šablona nám představí strukturu s funkcemi OnInit(), OnDeinit() a OnTick(). Kód, který jsme v předchozím případě měli v OnStart(), můžeme zkopírovat do OnTick(). Tak bude zajištěno, že se exekuuje vždy při novém ticku. Ještě jedna úprava je ale potřeba. Funkci Alert() musíme nahradit funkcí Comment(), aby byl splněn požadavek zobrazování v grafu. Má to ale i jiný dobrý důvod, který vám bude jasný, když funkci Alert() v tomto programu necháte a spustíte jej. Malou nevýhodou je, že aby se informace za současné podoby kódu zobrazila, musí se počkat na první příchozí tick. To může být nepříjemné na méně likvidních trzích, nebo když z nějakého důvodu testujeme či analyzujeme v době, kdy jsou trhy zavřené. Lze to vyřešit tak, že kód funkce OnTick() nakopírujeme i do funkce OnInit(). Tento kód se exekuuje již při spouštění programu, hodnota proto bude vypočítána ještě před příchodem prvního ticku. Pro zefektivnění programu je potom lepší ze společných proměnných oběma funkcím (v tomto případě Mezisoucet, Soucet, Prumer a x) vytvořit globální proměnné, které se tak stanou přístupnými pro všechny funkce programu a není třeba je dvakrát deklarovat a inicializovat. Toho dosáhneme přesunutím dvou řádků s deklaracemi do hlavičky programu. Odpovídající dva řádky potom můžeme smazat z obou funkcí. Obrázek hlavičky uvidíme v dalším zadání.

  1. Vylepšete program tak, aby trader měl možnost sám si zvolit počet period, za kterou se bude průměr počítat a aby výsledná hodnota byla zaokrouhlená na dvě desetinná místa.

První požadavek vyžaduje jednoduchou úpravu. Stačí místo konstanty 20 použít globální proměnnou, která bude deklarována v hlavičce programu a vložit před ní modifikátor input. Ten zajistí, že se proměnná objeví v seznamu vstupů, které zadává uživatel. Číslovku 20 pak v programu jednoduše nahradíme názvem této proměnné. Zaokrouhlení výsledné hodnoty dosáhneme buďto s pomocí funkce NormalizeDouble(), DoubleToStr() nebo DoubleToString(). Funkce NormalizeDouble() mi však občas z nějakého důvodu nefungovala správně, používám proto druhé dvě jmenované. Tělo v rámci funkcí OnInit() a OnTick() po obou úpravách může vypadat takto:

Hlavička programu je následující:

Sestavili jsme první smysluplný program, který si můžete vylepšovat a rozšiřovat podle svých potřeb a fantazie. Program můžete snadno doplnit například informacemi o otevřených či uzavřených pozicích, o spreadu, o času do konce svíčky a další a další funkcionality. Můžete také dovolit uživateli, aby si zvolil velikost písma, barvu a další vlastnosti, s jakými se informace bude zobrazovat. To by si vyžádalo opuštění funkce Comment(), vytvoření grafického objektu typu text a v případě změny hodnoty průměru, změnu vlastnosti tohoto objektu. Pomalu se blížíme k tématu, kvůli kterému tuto sérii píšu a kvůli kterému bylo potřeba absolvovat všechny lekce, a totiž k automatickým obchodním systémům. Spolu s Metatraderem si totiž stahujeme i dva ukázkové jednoduché obchodní systémy přímo od Metaquotes. Právě ony dva si rozebereme a vysvětlíme si jednotlivé segmenty, které lze použít i ve vašich vlastních obchodních systémech.

Rozbor obchodního systému I

Konečně jsme se dostali  ke konkrétním obchodním systémům. Předpokládáme, že pokud se učíte programovací jazyk MQL, je vaším cílem právě programování obchodních systémů. AOS rozhodně přinášejí vašemu tradingu nové možnosti a sama automatizace jako taková v sobě nese spoustu výhod. Automatický backtest je rozhodně pohodlnější než ruční a během několika vteřin prozradí, zda má smysl danou myšlenku dále rozvíjet. V posledních dvou dílech si rozebereme dva obchodní systémy, které byste po stažení Metatraderu měli najít i mezi svými soubory. Jmenují se Moving Average a MACD Sample a nejdete je v Experts ve složce dat vaší platformy (jejíž obsah uvidíte po spuštění MetaEditoru). Poskytují inspiraci přímo od Metaquotes, jak by mohl jednoduchý AOS v Metatraderu se všemi náležitostmi vypadat, zároveň ale dávají najevo, že každý obchodní systém může být postaven naprosto odlišně. Začneme jednodušším MACD Sample. Kód rozdělíme na několik částí. Pod obrázkem vždy následuje popis dané části, čísla v závorkách označují pro lepší orientaci čísla řádků kódu dle obrázku.

Hlavička obsahuje kromě informací o autorovi (6-7) externí proměnné (9-14), které má zadat uživatel obchodního systému. Jedná se o proměnné money managementu a risk managementu a o nastavení parametrů indikátoru MACD.

Následuje funkce OnTick(), kde na začátku jsou klasicky deklarovány proměnné, které se ve funkci použijí (20-23). Po skupině komentářů (24-31) následuje kontrolní blok, který zamezuje zbytečné exekuci programu (32-41). Není nutný, ale je vhodné nějaký podobný ve svém programu mít. V tomto případě se exekuce hned na začátku ukončí, pokud je v grafu méně než 100 svíček anebo pokud uživatel zadal příkaz Take Profit menší než 10. Program toto vyhodnotí jako chybu, napíše o tom záznam do programového logu funkcí Print() (34 a 39) a ukončí exekuci. V dalším bloku jsou vypočítány hodnoty, za základě kterých se bude program rozhodovat, zda otevřít nebo uzavřít pozici, a jsou uloženy do proměnných (43-48). Autor nám zároveň vysvětluje proč – zjednodušuje to kód a zrychluje exekuci (42). Konkrétně jde o současné a předchozí hodnoty indikátoru MACD a klouzavého průměru.

Následuje konečně blok rozhodování o otevření pozice (50-88). Vše je uloženo v podmínce, která se ptá, zda je již nějaká pozice otevřena (51). Z toho plyne, že tento AOS dovoluje mít otevřenou vždy jen jednu pozici. Faktem ale je, že i kdyby tam tato podmínka nebyla, více pozic než jednu by ani ze své podstaty otevřít nemohl. S touto podmínkou se ale opět vyhneme zbytečné exekuci některých operátorů, když už bude pozice otevřena. Další podmínka se týká dostupné marže (54-58). Pokud nemáme dostatek prostředků, nemá smysl dále pokračovat a zkoumat, zda přišel signál pro otevření pozice. Uzavření pozice je ale mimo tuto podmínku, což je v pořádku.

Po kontrole dostupných prostředků nás čeká konečně rozhodovací mechanismus otevření pozice. Pokud je splněna série podmínek, která odpovídá nákupnímu signálu (60-61), je zavolána funkce OrderSend() s určitými parametry (63). Pokud se podaří pozici otevřít, dostane číslo tiketu a uloží se do proměnné ticket (stále 63). Když se tak stane, v logu se objeví hláška o úspěšném otevření pozice s číslem příslušného tiketu (64-68). Pokud se někde objeví chyba, rovněž je o tom odeslána informace do logu. Pomocí funkce GetLastError() je chyba v takovém případě identifikována a exekuce ukončena (69-71). Pokud je totiž splněna podmínka pro long, těžko bude splněna také pro short. Následuje totožný blok, který se exekuuje v případě signálu pro short (74-87). První důležitý blok tímto máme za sebou. Jak však autoři správně v komentáři poznamenávají (89), vstoupit do trhu správně je důležité, ale ještě důležitější je z něj správně vystoupit. Čeká nás tedy blok, který kontroluje otevřenou pozici a ověřuje, zda není čas ji uzavřít, případně upravit.

Vše je uzavřeno v cyklu for (90-151), který má za úkol udělat tolik iterací, kolik je otevřených pozic. Možná si řeknete „proč, když může být otevřena vždy jen jedna pozice?“. Z toho důvodu, že otevřené pozice v terminálu nemusí pocházet jen z činnosti tohoto obchodního systému. My však chceme prohledávat pouze tyto. V první podmínce (92-93) načítáme konkrétní pozici do paměti. Pokud se načtení nepodaří, operátor continue zahájí rovnou další iteraci cyklu. Pokud se podaří načíst pozici, je zjištěno, zda se nejedná o limitní příkaz (94) a z jakého instrumentu pochází (95). Pokud se jedná o long (98), podíváme se na něj blíže. Pak už se konečně kontroluje, zda jsou splněny podmínky pro uzavření obchodu (101-102). Pokud ano, pozici uzavřeme, zapíšeme o tom záznam a ukončíme exekuci (105-107). Pokud ne, autoři vložili kód, který funguje jako TrailingStop (110-122). Pokud je uživatelem úroveň Trailing Stopu zadán (110), zkontroluje se, jestli je obchod v dostatečném zisku (112) a jestli je nad případným stoplossem (114). Je-li tomu tak, pozice se modifikuje a exekuce ukončí (117-119). Pokud je podmínka na řádku 98 vyhodnocena jako neplatná, je jasné, že náš příkaz je short a následuje analogický blok 124-149, kde je rozhodováno o jeho uzavření a o posunutí stoplossu.

Rozbor obchodního systému II

Druhý ze zmiňovaných ukázkových obchodních systémů, Moving Average, funguje v zásadě podobně, ale je zkonstruován zcela odlišně. Princip fungování obou systémů je podobný, řídí se tradičním použitím indikátoru MACD, respektive překřížením klouzavého průměru cenou. Proč rozebíráme zrovna tyto dva? Kromě toho, že je má v platformě každý, je jedním z důvodů ten, že z hlediska programátorského jsou napsány úplně jiným způsobem. Čtenář tak získá de facto dva odlišné pohledy na jednu věc, což by mělo rozvinout kreativitu a poskytnout inspiraci. Rozbor budeme provádět stejně jako v minulém díle – řádek po řádku tak, jak program kód exekuuje. Popis vždy následuje pod obrázkem rozebírané části. V závorkách jsou pro přehlednost uvedeny příslušné řádky kódu, na které je odkazováno. Začneme samozřejmě hlavičkou.

V hlavičce jsou nejprve tradičně uvedeny informace o tvůrci kódu (1-8). Následuje příkaz #define, pomocí kterého dává autor pro jednoduchost jméno určité konstantě, která se v kódu objeví vícekrát (10), v tomto případě jako takzvané magické číslo obchodu. To se používá například pro odlišení obchodů pocházejících z jedné konkrétní strategie. V dalším bloku je výpis všech uživatelem zadávaných proměnných (12-16). Aby je mohl zadat uživatel, stačí před deklaraci vložit příkaz input. Na dalším řádku následuje funkce CalculateCurrentOrders(). My však víme, že program nejprve exekuuje speciální funkci OnInit(). Ta v programu úplně chybí, takže následuje speciální funkce OnTick().

Zde vidíme první podstatný rozdíl oproti systému MACD Sample. Celý kód MACD Sample k exekuci je umístěn uvnitř funkce OnTick(), kdežto zdejší funkce OnTick() se omezí jen na pár řádků. Nejprve program kontroluje, zda je v grafu více než 100 svíček a zda je povoleno automatické obchodování (138). Pokud je jedna z podmínek porušena, exekuce ihned končí (139). Následuje rozhodování. Pokud funkce CalculateCurrentOrders() s parametrem názvu symbolu grafu (ten vrací funkce Symbol()) vrátí nulu, je zavolána funkce CheckForOpen() (141). Pokud vrátí cokoliv jiného, je zavolána funkce CheckForClose() (142). Z těchto dvou řádků plyne, že strategie může mít naráz otevřený pouze jeden obchod, stejně jako tomu bylo u MACD Sample. Pojďme se nyní podívat na ony tři funkce, které jsou uvnitř této funkce volány.

Funkce CalculateCurrentOrders(), do které vstupuje parametr „symbol“ obsahující název grafu, na kterém strategie běží, je obyčejná uživatelská funkce, která má za úkol spočítat počet otevřených pozic. Toho dosahuje cyklem, který projíždí všechny pozice v tabulce obchod (tj. jak otevřené, tak čekající limitní). Pro účely cyklu je použito pomocné proměnné i (24). Po výběru pozice je zjištěno, zda je otevřena na stejném symbolu, na kterém běží strategie a zároveň zda je její magické číslo shodné s naším (27). Tím je zajištěno, že obchody, které prošly tímto sítem, byly otevřeny touto strategií na tomto grafu. Zbývá již jen zjistit, zda je daná pozice long (29) nebo short (30) a pokud ano, přičíst do příslušné proměnné jedničku. Vrácen bude počet longů nebo záporná hodnota počtu shortů (34,35). Získáme tak de facto expozici, kterou strategie vytvořila.

Tato funkce má, jak název napovídá, za úkol zjistit, zda jsou v trhu podmínky pro otevření pozice. Zajímavý je řádek 78. Ten říká, že pokračovat ve funkci se má pouze u prvního ticku nové svíčky. Jde tedy o obchodní systém, který exekuuje signály pouze na nové svíčce. V případě prvního ticku nové svíčky tedy program nejprve zjistí aktuální hodnotu klouzavého průměru s parametry, které zadal uživatel programu, a uloží ji do proměnné „ma“ (80). Následně se zjišťuje, zda byl překřížen klouzavý průměr a případně kterým směrem. Pokud open poslední hotové svíčky bylo výše než průměr, ale close bylo již níže (82), znamená to, že cena protnula průměr směrem dolů, což znamená prodejní signál (84,85). Opačná situace je řešena na řádcích 88-91. Pro výpočet objemu pozice je v obou případech zavolána funkce LotsOptimized(). Funkce představuje určitý nadstandard pro money management a v tomto díle ji rozebírat nebudeme.

Funkce, která kontroluje podmínky pro uzavření pozice, má v podstatě stejnou konstrukci jako CheckForOpen(). Kontrola opět probíhá jen při prvním ticku nové svíčky (102). Opět je zjištěna poslední hodnota klouzavého průměru (104) a porovnána s otevírací a zavírací cenou poslední svíčky (113 a 122). Nejdříve je ale potřeba vybrat pozici, kterou budeme kontrolovat pro uzavření. To probíhá podobným cyklem jako ve funkci CalculateCurrentOrders(). Rozdíl je pouze v druhé podmínce, která je pojata opačným způsobem (109). Pokud magické číslo nebo symbol grafu nesouhlasí s magickým číslem strategie a s grafem, na kterém běží, vynutí se nová iterace cyklu (přejde se na další pozici), neboť taková pozice nás nezajímá. Jakmile máme pozici, která nás zajímá, stačí zjistit, zda se jedná o long (111) nebo o short (120), a potom zkontrolovat, jak je na tom klouzavý průměr vůči ceně. Pokud cena překřížila průměr shora (113), je to signál k uzavření longu (115,116). Pokud jej překřížila zespoda (122), je to signál k uzavření shortu (124,125). Pokud je nějaká pozice uzavřena, cyklus lze ukončit (118,127), neboť strategie může mít otevřenou nanejvýš jednu pozici. Tím jsou u konce oba rozbory obchodních systémů.

Tester strategií

Máme-li jednou naprogramovaný obchodní systém nebo alespoň jeho základ, jistě bude vhodné jej otestovat na historických datech. Díky tomu zavčasu zjistíme, zda vůbec má smysl jeho další vývoj, ale také odhalíme případné programátorské chyby, které neodhalil kompilátor. Pokud je vše v pořádku, bude dobré také optimalizovat parametry obchodního systému, abychom zjistili nejvhodnější kombinaci parametrů a tím vytěžili maximum z jeho potenciálu. V těchto úkolech vám pomůže poslední díl této série o programování v jazyce MQL, který již nebude přímo o programování, ale logicky k této sérii patří.

Testování

Modul pro testování automatických obchodních systémů, ale také indikátorů na historických datech najdeme v terminálu pod názvem Tester strategií s ikonou . Po kliknutí na ní se pod grafy otevře následující okno:

V největším rozevíracím seznamu vybereme obchodní systém, který chceme testovat. Dále je potřeba zadat testovaný instrument, timeframe testovaného grafu a model neboli přesnost. Pokud nebude pro zadaný datum dostupný příslušný timeframe, tester vezme první vyšší. Čím přesnější model vybereme, tím déle bude test trvat, ale tím spolehlivější výsledky obdržíme. Dále můžeme nastavit datum, od kterého, případně do kterého bude systém podroben testu. Pokud checkbox Použít datum odškrtneme, použije se veškerá dostupná historie. Můžeme si také zvolit konkrétní výši spreadu, pokud nechceme použít aktuální spread instrumentu. Checkbox vizuální zobrazování použijeme v případě, že test chceme vidět přímo v grafu, šoupátkem potom nastavíme rychlost zobrazování. Checkbox Optimalizace rozebereme později, při běžném testu ho nezaškrtáváme. V pravé části testeru se nachází několik tlačítek. To nejdůležitější, Vlastnosti strategie…,  je hned nahoře. Odkáže nás na okno, kde vybíráme tak důležité parametry testu jako počáteční kapitál, měnu a parametry obchodního systému, což jsou hodnoty externích proměnných, které zadává uživatel. Na ostatní nastavení se podíváme za okamžik. Smysl zbývajících tlačítek by měl být zřejmý. Jakmile máme vše potřebné nastaveno, test odklepneme tlačítkem Začátek. Jako příklad otestujeme úspěšnost ukázkového obchodního systému MACD Sample, který byste měli nalézt i ve svém Metatraderu. Kód tohoto obchodního systému jsme rozebírali v předminulém díle. Podívejme se, jak se mu dařilo v roce 2015 na německém indexu DAX, a to nejpřesnějším možným způsobem. Počáteční kapitál ponecháme 10 000 USD. Parametr TakeProfit nastavíme na 1000 (100 bodů), TrailingStop na 500, ostatní parametry ponecháme ve výchozím nastavení.

Rychlost testu závisí na zvolené přesnosti a na výpočetním výkonu počítače. V tomto smyslu je škoda, že Metatrader 4 dokáže využívat pouze jedno jádro procesoru počítače. Po dokončení se ozve legrační výchozí zvuk a v modulu přibydou tři nové záložky. Výsledky -  Zde se objeví výpis všech příkazů tak, jak by je strategie postupně generovala – otevření pozice, modifikace i uzavření s různými podrobnostmi.

Graf – V této záložce uvidíme vývoj kapitálu účtu reprezentovaný modrou křivkou (česky pojmenováno jako zůstatek). V případě, že obchodní systém generuje vysoké otevřené zisky/ztráty, může být zřetelná také zelená křivka, která započítává právě otevřený stav účtu (majetek). Náš systém s danými parametry v počátku daného období víceméně stagnoval, potom začal ztrácet.

Přehled je nejdůležitější část vyhodnocení, na které vidíme všechny hlavní parametry testu, které můžeme dále analyzovat.

Optimalizace

Bylo by zdlouhavé dělat desítky testů s různými parametry, abychom zjistili, které parametry jsou pro daný systém a instrument nejvhodnější. Metatrader to udělá naštěstí za nás, my mu musíme jen říct, které parametry chceme optimalizovat, jaké budou jejich hraniční hodnoty a jak veliké budou kroky, po kterých se budou parametry měnit. Terminál pak provede sérii testů a zobrazí výsledky, ze kterých vyplyne, jaké výsledky vykazuje ta která kombinace parametrů. Samozřejmě bychom neměli systémy optimalizovat pouze podle zisku, ale spíše podle risk faktorů jako maximální drawdown apod. To je ale součástí jiného povídání o tradingu. Dejme tomu, že chceme zjistit vhodné nastavení profit targetu a stop lossu pro obchodní systém MACD Sample na DAXu za minulý rok. Nastavení zůstane stejné, jaké jsme použili při běžném testu, tentokrát ale zaškrtneme checkbox Optimalizace. Parametry optimalizace nastavíme ve Vlastnostech strategie. V záložce testování nastavíme optimalizovaný parametr. Ve výchozím nastavení je to Balance, tester tedy bude hledat kombinaci parametrů, která by přinesla nejvyšší zisk. My, protože jsme rizikově averzní, budeme optimalizovat drawdown účtu, nastavíme tedy Maximal Drawdown.

Na záložce Vložit parametry si tentokrát všimneme dalších sloupců, které nás při prostém testu nezajímaly. Zaškrtneme parametry, které chceme optimalizovat, v našem případě tedy TakeProfit a TrailingStop. Sloupec Hodnota nyní nehraje u optimalizovaných parametrů roli, důležité jsou další tři. V nich je potřeba nastavit nejnižší a nejvyšší testovanou hodnotu a po jak velkých krocích má tester postupovat. Čím menší bude krok, tím více kombinací bude tester provádět a tím déle bude celý proces trvat. Řekněme, že budeme chtít najít vhodnou kombinaci našich dvou parametrů, přičemž budeme chtít prověřit stoplosy 500,1000,1500 a 2000 a Profit targety od 1000 do 5000 po 500. V případě stoplossu se jedná o 4 hodnoty, u targetu o 9 hodnot. Celkem by tedy tester měl porovnat výsledky 4*9 = 36 kombinací. Tabulka parametrů bude ve správném nastavení vypadat následovně:

Na poslední záložce Optimalizace potom lze nastavit limity některých parametrů účtu. Jakmile máme vše potřebné nastaveno, klikneme na Začátek. Optimalizace pochopitelně trvá mnohem déle než samotný test. V našem případě provádí tester 36 testů za sebou a porovnává výsledky. V případě rozsáhlých optimalizací na podrobných datech se může protáhnout i na mnoho hodin. Po dokončení optimalizace přibydou další dvě záložky – Výsledek optimalizace a Graf optimalizace. Výsledek optimalizace – Zde vidíme tabulku s informacemi o výsledcích testů jednotlivých kombinací. Jsou seřazeny podle parametru, který jsme chtěli optimalizovat, a to od nejlepšího výsledku po nejhorší.

Graf optimalizace – zde uvidíme grafický výsledek optimalizace. Ten se může značně lišit podle toho, kolik parametrů optimalizujeme. Z našeho testu obdržíme graf, na jehož vodorovné ose vidíme pořadové číslo testu a na svislé maximální drawdown, tedy optimalizovaný parametr. Po najetí kurzorem na modrou kuličku se dozvíme, kterým parametrům daný výsledek odpovídá.

Jak s výsledky testů a optimalizací naložit, to už je úkol pro vás a pro jiné články a série. Já věřím, že tato série vám poskytla srozumitelný základ pro tvorbu, testování a vylepšování rozličných obchodních systémů, indikátorů a jiných šikovných prográmků v Metatraderu, které vám pomohou dosáhnout vašeho obchodního potenciálu. Pokud najdete obchodní systém, který bude splňovat risk parametry projektu FTMO, neváhejte přijmout naši Výzvu, neboť i šikovné vývojáře automatických systémů rádi prostřednictvím našeho projektu podpoříme.

Nejčastější chyby a jejich řešení

Málokdy se podaří napsat funkční program hned na první pokus. Většinou je potřeba nejprve vyřešit chyby, případně varování, na které nás upozorní kompilátor. Proto si představíme ty nejklasičtější a řekneme si, kde je jejich příčina a jak postupovat při jejich opravě. Na závěr se podíváme i na chyby, které vrací terminál nebo server brokera, tedy ve většině chyby při exekuci obchodních příkazů.

Chyby kompilátoru

Takto vypadá okno s výpisem logu kompilátoru. Okno se nachází v Panelu nástrojů MetaEditoru a při každé kompilaci na nás automaticky vyskočí. Řádky se znakem jednosměrky jsou chybami, které je třeba opravit před použitím programu. Dokud nebudou opraveny všechny chyby, nebude program možné ani spustit. Řádky se žlutým trojúhelníkem jsou potom varování, která nás upozorňují na možnou chybu nebo neefektivitu, ale program půjde i s nimi spustit. Poklepáním na řádek chyby/varování se kurzor přesune na místo chyby v kódu. Souřadnice jsou vypsány také ve sloupcích Řádek a Sloupec. Následující skupina chyb obvykle znamená chybu v syntaxi. Předpokládáme, že syntax známe, proto se obvykle jedná o překlepy a nepozornosti, které se snadno vyřeší.   Jde o část kódu, kde kontrola očekává nějaký výraz nebo název, ale nenajde jej tam. Může se jednat třeba o zapomenutou čárku tam, kde má být název proměnné pro deklaraci:   Semicolon je anglicky středník, proto tuto chybu získáme v případě, že zapomeneme za nějakou operací napsat středník. Chyba však ovlivní celou strukturu programu (podobně jako když nenapíšeme tečku se z jedné věty v článku mohou stát náhle tři) a může proto způsobit lavinu dalších chyb, které po opravení této zmizí. Tyto základní chyby v syntaxi je proto vhodné opravovat jako první a ve směru shora.   Tato chyba se objevuje tam, kde program očekává operátor a nenajde jej tam. Například v této části kódu chybu způsobila neuzavřená závorka, kdy kontrola očekávala pokračování podmínky, ale narazila už na složenou závorku, která označuje začátek těla operátoru.     Důsledkem je i následující chyba, která upozorňuje právě na neuzavřenou závorku:  Můžete narazit také na následující sérii chyb:

Ta vznikne, pokud program v hlavičce operátoru nenajde podmínku. Tradičně se to stává třeba při záměně porovnávacího operátoru „==“ a přiřazovacího operátoru „=“. Pokud se nám to stane právě v hlavičce například operátoru if-else, z podmínky se stane operace a vznikne tato chyba. Vzhledem k podobnosti obou výrazů je často obtížné odhalit takto banální problém.   Tato chyba znamená nesprávný počet parametrů funkce. Pokud nevíme, co s tím, je vhodné se podívat do dokumentace na výčet parametrů (docs.mql4.com).

Tyto chyby označují chybnou vrácenou hodnotu funkce. První případ nastane, pokud funkce nevrací nic, přitom není typu void (tedy musí něco vrátit). Řešením je operátor return s hodnotou v závorce, např. return(lots); přičemž typ vracené hodnoty musí odpovídat typu funkce. A naopak druhá chyba označuje případ, kdy nám funkce vrací hodnotu, přestože se jedná o typ void, který nic vracet nemá. Pak je řešením osamocený operátor return;.

Pokud return úplně chybí, dostaneme tuto chybu:

Tím program upozorňuje, že někde je v programu slepá ulička. Může to nastat právě při absenci operátoru return ve funkci, ale také třeba při následující situaci:

Funkce return je definována pro hodnotu proměnné buys větší než 0 a menší než -2. Co když bude ale buys rovno -2,-1 nebo 0? Taková akce není definována, a tak se opět program může dostat do slepé uličky.

Varování kompilátoru

Jak již bylo uvedeno, varování nevadí funkčnosti programu, upozorňují nás však na možné neefektivity programu, které občas ničemu nevadí, ale jindy mohou mít za následek jeho špatné fungování.   Toto upozornění se ukazuje, pokud používáme globální proměnné deklarované v hlavičce programu, ale zároveň nadeklarujeme stejný název proměnné uvnitř některé funkce. Deklarace uvnitř funkce potom nemá smysl, protože jak víme, globální proměnné jsou k dispozici všem funkcím programu.   Toto upozornění se týká především funkcí OrderSelect() a OrderSend() a znamená, že by měla být zkontrolována jejich vrácená hodnota. Obvykle se to řeší tak, že se funkce umístí do operátoru if a zkontroluje se, že třeba funkce OrderSend nevrací -1. Pokud totiž ano, znamená to, že otevření obchodu skončilo s chybou. Pak by bylo dobré se dozvědět, o jakou chybu se jednalo. To nám řekne jedině funkce GetLastError(). Chybový kód, který vrátí tato funkce je pak navíc třeba sdělit uživateli prostřednictvím funkcí Alert() nebo Print() apod. Dobrý příklad je v této části kódu:

Chybové kódy funkce GetLastError()

Na tomto místě si uvedeme nejčastější chybové kódy, které dostáváme při práci s pozicemi prostřednictvím funkce GetLastError() a jejich řešení. Pro celý seznam chybových kódů navštivte stránku https://book.mql4.com/appendix/errors. 3 (Invalid trade parameters): označuje obvykle chybu v parametrech obchodní funkce, jako chybný počet nebo chybné hodnoty. Řešení lze obvykle nalézt v kontrole požadovaných parametrů v dokumentaci. 129 (Invalid price): chybná cena, je potřeba zkontrolovat parametr Price obchodní funkce. Klasicky bývá chyba v záměně ceny Bid a Ask nebo v použití ceny jiného instrumentu, než na kterém se pokoušíme operovat s pozicí. 130 (Invalid stops): chybné výstupní příkazy stoploss a/nebo takeprofit. Klasicky jde o jejich záměnu, kdy např. v případě otevření short se snažíme nastavit stoploss pod otevírací cenu, což samozřejmě nejde. 131 (Invalid trade volume): chybný objem pozice. Tradičně když se pokoušíme otevřít například mikrolot na instrumentu, kde je nejnižší možná velikost pozice minilot. 132 (Market is closed): chyba pokud se pokoušíme otevřít pozici mimo obchodní hodiny daného instrumentu. 133 (Trading is disabled): situace, kdy terminál nebo strategie nemají povoleno obchodování. 134 (Not enough money): nedostatečný margin pro otevření nové pozice. 135 (Price changed), 136 (Off quotes), 138 (Requote): všechny chyby se vyskytují v případě skluzů a gapů při plnění příkazu, které jsou mimo naši nastavenou úroveň tolerance. 145 (Modification denied because an order is too close to market): spolu s chybou 130 může nastat v případě, kdy se pokoušíme umístit stoploss nebo profittarget příliš blízko aktuální ceně. Limit, co je ještě blízko a co už ne je u každého brokera jiný. Limit aktuálního brokera lze zjistit pomocí funkce MarketInfo(MODE_STOPLEVEL). Pokud si s chybou přece jen nebudeme vědět rady, vždy jsou k dispozici odkazy v tomto odstavci a případně také fórum komunity MQL (https://www.mql5.com/en/forum), kde vám s většinou dotazů poradí. Opět ale je potřeba umět anglicky nebo rusky.

Nemám chyby, program přesto nefunguje, jak má

Pokud jste vyřešili všechny chyby a varování kompilátoru i případné chyby, které vrací terminál nebo server brokera, stále nemusí program fungovat tak, jak byste si přáli. Třeba se ani nepokusí otevřít pozici tam, kde by podle vás měl. Jak postupovat v takovém případě? Předně je samozřejmě potřeba projít si znovu kód a zkontrolovat, jestli vše dává smysl. Pokud žádný problém neodhalíme, je potřeba testovat a zkoušet, dokud neodhalíme příčinu. Například pokud má za určitých podmínek otevřít pozici a neudělá to, musíme zkontrolovat, zda podmínky pro otevření skutečně byly splněny. K tomu je potřeba vyvolat proměnné, které v podmínce jsou, a to prostřednictvím funkcí Alert(), Comment() apod. před touto podmínkou. Jedině takto zjistíme, proč se program chová tak, jak se chová. Potom samozřejmě musíme dále pátrat po tom, proč je v určité proměnné hodnota jaká je a jak tam dostat tu správnou. Někdy jde o zdlouhavý a vyčerpávající proces, který nás ale zároveň vždy posune dál a do příště díky němu budeme vědět, na co si dát pozor. Pokud jste dočetli až sem, mělo by vám to stačit na pochopení základů jazyka a umožnit programovat základní programy i jednoduché obchodní systémy. Zároveň jste se dozvěděli, kde čerpat další informace, jak se orientovat v dokumentaci a jak řešit chyby. Dále už to bude spíše nadstavba pro pokročilejší programátory.

Ostatní speciální funkce

Doteď jsme zmínili pouze základní a nejčastěji používané speciální funkce (OnInit(), OnDeinit(), OnTick() a OnStart()). V novém MQL4 však přibylo oproti staré verzi jazyka několik nových speciálních funkcí, které mohou být velice užitečné. Jim se bude věnovat právě tato kapitola. Ještě před jejich výkladem začneme malým výletem do historie. Stará verze jazyka rozlišovala pouze 3 speciální funkce, a to init(), start() a deinit(), se kterými se stále můžete setkat ve starších programech a i nová verze MQL4 je stále z důvodu zpětné kompatibility respektuje. Init() a deinit() měly stejný význam jako jejich dnešní ekvivalenty s předponou On, tedy volaly se při spuštění respektive ukončení programu. Kdy se volala funkce start() potom záleželo na typu programu. Pokud se jednalo o skript, zavolala se při spuštění, tedy stejně jako dnešní OnStart(). Pokud se ale jednalo o strategii, volal se start() každý tick, stejně jako nynější funkce OnTick(). A konečně, pokud se jednalo o indikátor, volal se také při každém ticku, dnešním ekvivalentem je ovšem funkce OnCalculate(). Právě touto funkcí začneme.

OnCalculate()

Je obdobou funkce OnTick() pro program typu indikátor. Volá se také při každém ticku. Je to logické, protože kdy jindy je potřeba přepočítat hodnotu indikátoru než při nové příchozí ceně. I já jsem se samozřejmě ptal, jaký je rozdíl oproti funkci OnTick(). Vše tkví v parametrech a typu funkce. Zatímco OnTick() je typu void a nemá žádné parametry, OnCalculate() je typu int a má několik parametrů, které lze použít pro výpočty hodnot indikátoru. Díky tomu je funkce mnohem efektivnější pro programy typu indikátor. Pojďme se na ony parametry podívat.

První parametr rates_total odpovídá počtu svíček na grafu, kde je indikátor spuštěn. Umožňuje tak zabránit například zbytečným výpočtům. Druhý, velice důležitý parametr prev_calculated v sobě obsahuje hodnotu, která byla vypočítána při předchozí exekuci funkce OnCalculate(). Jeho použití umožní značné zefektivnění indikátoru, jelikož není nutné znovu přepočítávat hodnoty vypočítané pro svíčky, které se nezměnily od předchozího výpočtu. Pro většinu indikátorů to jsou všechny svíčky kromě té aktuální. Ostatní parametry v sobě nesou časové řady příslušných proměnných jednotlivých svíček přesně tak, jak je známe z kapitoly o proměnných. Jak vidno, parametry tvořené na míru programu typu indikátor dělají tento program značně efektivnější a jednodušší, než kdyby exekuci měla na starost čistě funkce OnTick(). Více se o OnCalculate() a programování indikátorů dozvíme v samostatné kapitole.

OnTimer()

Velice zajímavá speciální funkce, která v původní verzi MQL4 neměla obdobu. Jak plyne z názvu, tato funkce funkce je volána pravidelně po určitém čase. Umožňuje, aby část kódu v jejím rámci byla exekuována pravidelně a nezávisle na dění na trhu nebo v platformě. Můžeme se tak třeba každou hodinu nechat emailem informovat o stavu účtu, každý den si poslat report apod. Možná si říkáte, že toto doteď šlo také celkem bez problému řešit. Že se změnil den poznám jednoduše třeba tak, že se změní výsledek funkce Day() a obdobně. Ale co když potřebuji nějakou úlohu řešit každé tři hodiny, dvě minuty, šest sekund nebo dokonce v řádech milisekund? A co když potřebuji, aby to fungovalo i o víkendu a mimo obchodní hodiny daného instrumentu? To vše je již možné právě díky funkci OnTimer(), která je volána pouze na základě uplynulého času, nezajímá ji, co se děje na trhu. Periodické volání funkce OnTimer(), pokud ji máme v programu, spustíme funkcí EventSetTimer(), jejímž parametrem je počet sekund, po kterých se má funkce OnTimer() volat. Pokud tvoříme program se zaškrtnutou volbou OnTimer(), průvodce nám vygeneruje EventSetTimer(60) do funkce OnInit(), kde by měl být, není to však nutností. Viděl jsem i programy, kde se timer spouštěl z OnTick() za určitých podmínek. Ukončení timeru provádí funkce EventKillTimer(), která je obvykle umístěna v rámci OnDeinit(), ale opět to není nutnost. Pokud na EventKillTimer() zapomeneme, timer je ukončen násilím při ukončení programu. Dobré je vědět také to, že první zavolání timeru proběhne až po uplynutí nastavené čekací doby, nikoli ihned po zavolání EventSetTimer(). Pokud potřebujeme rozlišovací schopnost timeru pod sekundu, existuje spouštěcí funkce EventSetMillisecondTimer(), jejímž parametrem je počet milisekund, po kterých se exekuuje OnTimer(). Závěrem dlužno dodat, že v rámci jednoho programu lze použít jen jeden timer, ale zároveň může běžet více timerů na více různých strategiích a tyto se mezi sebou nebudou nikterak prát.

OnChartEvent()

Tato funkce exekuuje kód v případě, že uživatel provede některou z definovaných událostí. Obvykle se jedná o události spojené s objekty na grafu, třeba vytvoření, posunutí, smazání apod., nebo prosté kliknutí, pohyb myši či stisknutí klávesy. Funkce má následující parametry.

Parametr id označuje druh události. Ostatní parametry potom záleží právě na typu události. Například pokud jde o klik myší, lparam bude x-ová souřadnice grafu, dparam y-ová a sparam se nepoužije. Tabulku s výčtem různých událostí a jim příslušných parametrů nalezneme na stránce https://docs.mql4.com/basis/function/events#onchartevent samozřejmě v angličtině. Zanedlouho ovšem i této funkci bude věnována samostatné kapitola, kde bude tabulka vysvětlena v češtině.

OnTester()

Je funkce, která je volána pouze při testování strategie na historických datech, a to před vykonáním funkce OnDeinit(). Je typu double a nemá žádný parametr. Jejím účelem je obvykle výpočet hodnoty určitého testovacího kritéria, které uživateli poskytne možnost seřadit si podle tohoto kritéria testy s různými parametry strategie (optimalizace). Článek přinesl základní přehled pokročilých speciálních funkcí, které přinesla aktualizovaná verze jazyka MQL4. Každá speciální funkce, včetně těchto, bude mít vlastní kapitolu.

Globální proměnné terminálu

Proměnné jsme již rozdělili na lokální a globální podle toho, ve které části programu jsou deklarovány. Proměnná deklarovaná uvnitř funkce platí pouze pro tuto funkci, kdežto proměnná, která je deklarována v hlavičce programu mimo těla všech funkcí, platí pro všechny jeho funkce. Příkladem může být třeba hlavička programu MACD Sample, který máme všichni ve svých Metatraderech:

Proměnné počínaje TakeProfit a konče MATrendPeriod jsou globálními proměnnými. Modifikátor input a jejich červená barva potom udávají, že jsou editovatelné uživatelem strategie. Ve zmíněné kapitole bylo ale také naznačeno, že existuje ještě jiný typ globální proměnné, a to globální proměnná terminálu. Takovou proměnnou mohou používat nejen všechny funkce jednoho programu, ale rovněž všechny ostatní programy konkrétní platformy Metatrader, a to i po jejím ukončení. Dalo by se tak říct, že jde o ještě globálnější proměnnou. Jde o velice užitečnou věc, v této kapitole se s ní proto naučíme pracovat. S globálními proměnnými terminálu se pracuje odlišně než s klasickými proměnnými. Je pro ně vyhrazena celá sada funkcí, která zajišťuje vše od deklarace přes inicializaci a vyvolání hodnoty až po případné vymazání. Název každé funkce pro práci s těmito proměnnými začíná řetězcem GlobalVariable…(), kde místo teček je činnost, kterou chceme s proměnnou provést. Poněkud odlišně od klasických proměnných funguje deklarace a inicializace. Ta zde probíhá vždy naráz. Proměnnou deklarujeme i inicializujeme (vytvoříme a vložíme do ní hodnotu) funkcí GlobalVariableSet(), jejímž prvním parametrem je název proměnné a druhým hodnota, kterou ji chceme přidělit. Pokud proměnná s takovým názvem neexistuje, vytvoří se a dostane danou hodnotu, pokud existuje, pouze se změní její hodnota. Funkce tedy proměnné může deklarovat, inicializovat i měnit jejich hodnotu. Další důležitou funkcí pro práci s tímto druhem proměnných je GlobalVariableGet(). Jediným parametrem je název proměnné. Funkce vrátí hodnotu uloženou v proměnné. Jedině touto funkcí můžeme s hodnotou proměnné pracovat. Tyto dvě funkce vám teoreticky na práci s globálními proměnnými terminálu stačí, ale jazyk MQL jich nabízí ještě několik. Všechny popíšeme v následující tabulce:

Název funkce Akce
GlobalVariableCheck Ověří existenci proměnné s daným název
GlobalVariableTime Vrátí čas posledního přístupu na proměnnou s daným názvem
GlobalVariableDel Smaže proměnnou
GlobalVariableGet Vrátí hodnotu proměnné
GlobalVariableName Vrátí název proměnné podle jejího pořadového čísla v seznamu proměnných
GlobalVariableSet Nastaví proměnné novou hodnotu
GlobalVariablesFlush Uloží hodnoty všech proměnných na disk
GlobalVariableTemp Nastaví proměnné novou hodnotu, která existuje jen v aktuálním spuštění terminálu
GlobalVariableSetOnCondition Nastaví proměnné novou hodnotu na základě podmínky
GlobalVariablesDeleteAll Smaže všechny globální proměnné s danou předponou v jejich názvu
GlobalVariablesTotal Vrátí počet všech globálních proměnných

Jejich bližší popis a výčet parametrů najdete v angličtině na této stránce. Jak bylo uvedeno, globální proměnná si ponechává svou hodnotu i po uzavření platformy. Přesto se ale její hodnota neuchovává věčně. Konkrétní proměnná se vymaže, pokud se po dobu 4 týdnů její hodnota nezmění, ani se nevyvolá pomocí GlobalVariableGet().

Nové typy dat v aktualizovaném jazyce MQL4

S aktualizovaným jazykem MQL4 přišlo také mnoho nových typů dat. Většinou se ale jedné pouze o odvozené typy již existujících dat, aby bylo možno efektivněji využívat výpočetní zdroje. Ve starém MQL bylo možné rozlišit pouze následujících 6 typů dat:

Identifikátor Anglicky Česky
int integer celé číslo
bool logical pravda/nepravda
string string řetězec
double floating-point number desetinné číslo
color color barva
datetime date and time datum a čas
enum enumeration výčet

Jsou to typy čtenářům základní série dobře známé, setkali jsme se s nimi v kapitole Typy dat. Známé jsou i všem programátorům, ne každý už ale ví o odvozených typech dat. Jejich neznámost nebo nepoužívání obvykle nevede k žádným problémům. V případě programů náročných na výkon ale mohou ušetřit značné množství výpočetního výkonu a program tak zefektivnit. V jiných případech nesprávné použití typů dat může vést dokonce k chybám. Následující tabulka popisuje nové typy dat, které přišly s aktualizací jazyka MQL4. Všechno to jsou odvozeniny typu int a liší se pouze tím, kolik paměti si berou. To se projeví v rozsahu hodnot, který je do nich možné uložit.

Identifikátor Nejnižší hodnota Nejvyšší hodnota Paměť
char -128 127 1 byte
uchar 0 255 1 byte
short -32 768 32 767 2 byty
ushort 0 65 535 2 byty
int -2 147 483 648 2 147 483 647 4 byty
uint 0 4 294 967 295 4 byty
long -9 223 372 036 854 775 808 9 223 372 036 854 775 807 8 bytů
ulong 0 18 446 744 073 709 551 615 8 bytů

Je zřejmé, že podle toho, jaké hodnoty očekáváme v dané celočíselné proměnné můžeme pracovat s pamětí. Pokud moje proměnná bude nabývat pouze hodnot od jedné do deseti, nemusím se obávat jí přidělit typ char nebo uchar. Naopak u jiných proměnných, například číslo loginu u některých brokerů, už nemusí stačit ani typ int a bude potřeba long nebo ulong. Novým typem dat je ale také enum (výčet), který jsme již krátce nakousli ve zmiňovaném pátém díle. V této kapitole je možnost se na něj podívat blíže a naučit se s ním pracovat.

Výčet (enum) Tento typ dat slouží k vytvoření skupiny dat stejného typu. Datům můžeme přidělit hodnoty, ale nemusíme, pak budou přiděleny automaticky. Výčet musí být deklarován uvnitř složených závorek.

Příklad: enum Dny {Pondeli=1,Utery,Streda,Ctvrtek,Patek=5,Sobota,Nedele}; Možné použití výčtu si ukážeme na příkladu vytvoření roletky pro zadávání parametru uživatelem programu. Následující skript sečte dvě zadané hodnoty, přičemž uživatel má možnost z roletky vybrat pouze hodnoty 1 až 5 dané proměnnými Jedna až Pět.

 

Podle toho, co uživatel vybere v roletce, taková hodnota se uloží do jednotlivých sčítanců. Číselné ekvivalenty jednotlivým položkám enum můžeme přidělit již při deklaraci. Položky musí být typu int, tedy celočíselné. Pokud je nepřidělíme, přidělí se indexy automaticky, kdy první hodnota bude mít 0, druhá 1 atd. Aby uživateli vznikla roletka, za identifikátor input vložíme název výčtu a teprve poté proměnnou, do které bude vybraná hodnota uložena.

 

Co dělá FTMO?

Společnost FTMO vyvinula dvoufázový Ověřovací proces, který nám pomáhá s vyhledáváním talentovaných traderů. Po jeho absolvování můžete obchodovat na FTMO Accountu se zůstatkem až 4 000 000 Kč. Jak to funguje?.