TradeLib - Knihovna pro práci s pokyny
Ještě než začneme psát hlavní část strategie, napíšeme si pomocnou knihovnu obecných funkcí pro práci s pokyny, kterou můžeme kdykoli použít při vývoji dalších strategií. Kompletní souboru je ke stažení na konci kapitoly. Pro ty, co si jej chtějí vytvořit sami, následuje postup.
1. Vytvoření souboru
Začneme vytvořením Include skriptu. V MetaEditoru založte nový skript kliknutím v menu na File -> New, nebo klávesovou zkratkou Ctrl+N a v průvodci vyberte položku Include (*.MQH). Include soubory jsou sady funkcí, které lze připojit k jiným skriptům.
Klikněte na tlačítko Další a v dialogu vyplňte jméno skriptu TradeLib.
Veškeré komentáře, které průvodce vygeneroval, můžete smazat a ponechat kód ve stavu, jaký zobrazuje obrázek.
2. Funkce IsTicketOpen()
Tato funkce zjistí, zda je konkrétní pokyn ještě otevřen, nebo ne. Může se stát, že se pokyn zavře překročením ceny StopLoss nebo TakeProfit, případně pokyn zavře broker. Potom je třeba vědět, zda lze s pokynem ještě něco vykonávat v rámci obchodu.
// Zjistí, zda je pokyn otevřený
// ticketId = identifikátor pokynu
bool IsTicketOpen(int ticketId)
{
if(OrderSelect(ticketId, SELECT_BY_TICKET))
{
return(OrderCloseTime() == 0);
}
else
{
return(false);
}
}
Funkce přejímá jeden parametr a to ID pokynu a vrací True , pokud je pokyn otevřen, jinak False. Tělo funkce se skládá z podmínky, kde pomocí funkce OrderSelect() vybereme konkrétní pokyn dle jeho ID. Pokud funkce proběhne v pořádku, zjistíme pomocí funkce OrderCloseTime(), zda je datum zavření pokynu nastaveno nebo ne. Pokud není, pokyn je ještě otevřen.
3. Funkce OpenTicket()
Tato metoda provede otevření pokynu a vrátí jeho ID. Metoda uvažuje pouze přímé pokyny. Pokud potřebujete otevřít čekající pokyn, je třeba si do knihovny napsat svou vlastní podporu.
// Otevře nový pokyn
// symbol = identifikace měnového páru
// isBuy = pokud je True, nakupuje se, jinak se prodává
// lots = počet lotů, které se má obchodovat
// slPips = počet pipů pro nastavení ceny stoploss
// tpPips = počet pipů pro nastavení ceny takeprofit
int OpenTicket(string symbol, bool isBuy, double lots, int slPips, int tpPips)
{
double price; // preferovaná cena
int op; // typ operace
// cena stoplossu
double slPrice = getPipPrice(symbol, slPips, isBuy, true);
// cena takeprofitu
double tpPrice = getPipPrice(symbol, tpPips, isBuy, false);
// nastavíme preferovanou cenu a typ operace
if(isBuy)
{
price = MarketInfo(symbol, MODE_ASK);
op = OP_BUY;
}
else
{
price = MarketInfo(symbol, MODE_BID);
op = OP_SELL;
}
int ticketId = OrderSend(symbol, op, lots, price, 0, slPrice, tpPrice, NULL, 0, 0, CLR_NONE);
// pokud se pokyn nepodaří otevřít, vypíšeme číslo chyby do logu
if(ticketId == -1)
{
Print("Otevírání pokynu: Chyba č. ", GetLastError());
}
return(ticketId);
}
Nejdříve si nadeklarujeme pár proměnných jako je preferovaná cena a typ operace. Pak stanovíme cenu pro stoploss a takeprofit. K tomu použijeme pomocnou funkci getPipPrice(), kterou si popíšeme později. Dále nastavíme cenu a typ operace. V kódu se pro vás objevuje nová funkce MarketInfo(). Tato funkce zjišťuje informace o cenách a vrací stejné informace jako předdefinované proměnné (Ask, Bid, Digits, atd.). Ale protože píšeme obecnou funkci, nemůžeme se spoléhat na to, že jiný skript bude uvažovat stejný měnový pár, jaký by vrátila funkce Symbol(). Proto naše funkce vyžaduje zadat, pro jaký měnový pár se daná operace má vykonat a pomocí funkce MarketInfo() si již zjistíme, co potřebujeme. Po nastavení správných cen, již zavoláme funkce pro otevření pokynu OrderSend(). Pokud se nepodaří otevřít pokyn, zalogujeme chybu, pro případné pozdější zkoumání.
4. Pomocná funkce getPipPrice()
Tato funkce spočítá cenu StopLoss a TakeProfit na základě počtu bodů, které uživatel zadal.
// Vrátí cenu pro daný počet pipů vůči aktuální ceně
// symbol = identifikace měnového páru
// pips = počet pipů
// isBuy = určuje, zda se jedná o nákup
// isSL = určuje, zda se jedná o SL, jinak je to TP
double getPipPrice(string symbol, int pips, bool isBuy, bool isSL)
{
double price; // obchodní cena
double result = 0; // výsledná hodnota
if(pips > 0)
{
// funkce defaultně počítá cenu pro SL
// pokud se jedná o TP, otočíme znaménko pro výpočet
if(isSL == false)
pips = pips * (-1);
if(isBuy)
{
price = MarketInfo(symbol, MODE_ASK);
result = NormalizeDouble(price - pips * MarketInfo(symbol, MODE_POINT),
MarketInfo(symbol, MODE_DIGITS));
}
else
{
price = MarketInfo(symbol, MODE_BID);
result = NormalizeDouble(price + pips * MarketInfo(symbol, MODE_POINT),
MarketInfo(symbol, MODE_DIGITS));
}
}
return(result);
}
Pomocí funkce MarketInfo() si zjistíme potřebné hodnoty a pak dle vzorce vypočítáme cenu na základě předaných parametrů.
5. Funkce CloseTicket()
Když už máme otevřený pokyn, je třeba napsat funkci, která jej také zavře. Funkce zavírá celý objem obchodu. Pokud potřebujete částečné zavírání pokynu, je třeba si funkci upravit nebo napsat novou.
// Pokusí se zavřít pokyn. Vrací 0, pokud je vše v pořádku, jinak vrací číslo chyby
// ticketId = identifikace pokynu
int CloseTicket(int ticketId)
{
if(IsTicketOpen(ticketId))
{
double price; // preferovaná cena zavření
// nastavíme preferovanou cenu dle typu operace
if(OrderType() == OP_BUY)
price = MarketInfo(OrderSymbol(), MODE_BID);
else
price = MarketInfo(OrderSymbol(), MODE_ASK);
if(OrderClose(ticketId, OrderLots(), price, 0, CLR_NONE))
{
return(0);
}
else
{
// chyba při zavírání
Print("Zavírání pokynu: Pokyn č. ", ticketId, " se nepodařilo zavřít. Chyba č. ",
GetLastError());
return(2);
}
}
else
{
// Pokyn je již zavřen nebo nebyl nalezen
Print("Zavírání pokynu: Pokyn č. ", ticketId, " již nelze zavřít");
return(1);
}
}
Nejdříve zjistíme, zda je pokyn otevřen. K tomu se nám hodí funkce IsTicketOpen(), která zároveň provede výběr pokynu. Pokud je pokyn otevřen, zjistíme typ operace, nastavíme cenu a pomocí funkce OrderClose() pokyn zavřeme. V případě, že operace neproběhne v pořádku, zapíšem chybu do logu.
6. Funkce IsTimeToTrade()
Tato funkce zjistí, zda je čas pro otevírání pokynů. V parametrech přejímá kritéria času v jednom rozptylu. Funkci lze např. napsat, tak, aby kontrolovala sérií časových rozmezí, ale pro náš příklad bohatě postačí.
// Určuje, zda je čas pro obchodování
// startHour = hodina pro zahájení obchodování
// startMinute = minuta pro zahájení obchodování
// stopHour = hodina pro ukončení obchodávní
// stopMinute = minuta pro ukončení obchodování
bool IsTimeToTrade(int startHour, int startMinute, int stopHour, int stopMinute)
{
// zjistíme aktuální čas
datetime currentTime = TimeCurrent();
// převedeme si časy na minuty
int start = startHour * 60 + startMinute;
int stop = stopHour * 60 + stopMinute;
int current = TimeHour(currentTime) * 60 + TimeMinute(currentTime);
return(current >= start && current <= stop);
}
Ve funkci si převedeme čas na minuty a porovnáme, zda spadá mezi zadané limity.
7. Funkce TrailStopLoss()
Tato funkce má za úkol posouvat cenu StopLoss daného pokynu na základě zadaných parametrů.
// Provede posunutí ceny StopLoss
// ticketId = ID pokynu
// pipsDiff = počet pipů, po kolika se má posunout SL
// pips = počet pipů, na kolik se má nastavit SL
// useBE = určuje, jestli se má při prvním posunu nastavit SL na BE
void TrailStopLoss(int ticketId, int pipsDiff, int pips, bool useBE)
{
if(IsTicketOpen(ticketId))
{
string symbol = OrderSymbol();
double pointValue = MarketInfo(symbol, MODE_POINT);
double openPrice = OrderOpenPrice();
double slPrice = OrderStopLoss();
double currentPrice;
double newSL = 0;
// zjistíme aktuální cenu
if(OrderType() == OP_BUY)
{
currentPrice = MarketInfo(symbol, MODE_ASK);
if(useBE && (openPrice > slPrice))
{
// zjistíme, zda je čas posunout SL na BE
if(currentPrice > (openPrice + pointValue * pipsDiff))
newSL = NormalizeDouble(openPrice, MarketInfo(symbol, MODE_DIGITS));
}
else
{
// zjistíme, zda je čas posunout SL
if(currentPrice > (slPrice + pointValue * pipsDiff + pointValue * pips))
newSL = NormalizeDouble(currentPrice - pointValue * pips,
MarketInfo(symbol, MODE_DIGITS));
}
}
else
{
currentPrice = MarketInfo(symbol, MODE_BID);
if(useBE && (openPrice < slPrice))
{
// zjistíme, zda je čas posunout SL na BE
if(currentPrice < (openPrice - pointValue * pipsDiff))
newSL = NormalizeDouble(openPrice, MarketInfo(symbol, MODE_DIGITS));
}
else
{
// zjistíme, zda je čas posunout SL
if(currentPrice < (slPrice - pointValue * pipsDiff - pointValue * pips))
newSL = NormalizeDouble(currentPrice + pointValue * pips,
MarketInfo(symbol, MODE_DIGITS));
}
}
if(newSL > 0)
{
if(OrderModify(ticketId, openPrice, newSL, OrderTakeProfit(), 0, CLR_NONE) == false)
{
Print("Chyba při posouvání ceny StopLoss, chyba č. ", GetLastError());
}
}
}
}
Nejdříve zjistíme, zda je pokyn otevřen, poté zjistíme potřebné hodnoty pro výpočet nové ceny SL. Pokud je parametr useBE nastaven na True, nastavuje se cena SL na BE (Break Even – cena na hranici otevírací ceny, kdy obchodník již nemůže přijít o svou investici) a to pouze pokud je cena SL stále pod otevírací cenou. Poté se již matematicky vypočítá nová hodnota SL a upraví pokyn.
Kompletní soubor TradeLib.mqh si můžete stáhnou zde.