Programowanie
Teraz czytasz
MQL Praktycznie. Panic Button, część II [Kurs programowania]
0

MQL Praktycznie. Panic Button, część II [Kurs programowania]

utworzył Radek Szafron29 marca 2019

Witajcie w drugiej części Panic Button! W tym tygodniu ponownie zerkamy do kodu naszego przycisku do spraw paniki. Naszym dzisiejszym zadaniem będzie dodanie tego co najważniejsze, czyli funkcji uwalniania nas od ciężaru otwartych pozycji i oswobadzania z więzów ciągłego wpatrywania się w monitor.

Podstawową funkcjonalność rozwiniemy o coś jeszcze. Zadbamy o to, aby projektowane przez nas narzędzie było bezpieczne i przypadkowe kliknięcia przyjmowało niewzruszone i ze stoickim spokojem. Jak każdy poważny przycisk na naszej planecie i nasz będzie posiadał swojego rodzaju bezpiecznik, który umożliwi zamknięcie pozycji tylko i wyłącznie, gdy kliknięciu towarzyszyło będzie wciśnięcie klawisza [ Shift ] na klawiaturze.

Przeczytaj koniecznie: MQL Praktycznie. Panic Button, część I

Otwieramy fabrykę algorytmów

Czyli włączamy MetaEditor.

Przejdźmy do edycji kodu naszej aplikacji z zeszłego tygodnia. Otwórzmy plik Panic Button.mq4 z katalogu Experts i rzućmy okiem na funkcję OnInit(). Wasza funkcja powinna wyglądać niemalże identycznie jak funkcja umieszczona poniżej z małą różnicą. Dziś pojawił się nowy element kodu oznaczony // komentarzem .

int OnInit()
{
if(IsDemo() == false)
{
 
return INIT_FAILED;
 
}
bool niebieski = false;
if(Tylko_ten_instrument == true)
{
 
niebieski = true;
 
}
Przycisku_utworz_sie(5, 15, niebieski);
// <– [ Tak robimy komentarze ] EventSetMillisecondTimer(250); // Koniec nowości
return INIT_SUCCEEDED;
}
Kod MQL4

Dodajmy brakujący kod do funkcji OnInit().

Uruchamiamy zegar

Dodana do kodu powyżej funkcja MQL API bool EventSetMillisecondTimer(int milisekundy)pozwala skonfigurować timer. Timer to metoda pozwalająca na wywoływanie innej funkcji API – OnTimer() co określoną liczbę milisekund. Takie rozwiązanie pozwala nam cyklicznie uruchamiać określone części kodu poprzez umieszczenie ich wewnątrz funkcji OnTimer(). W naszym przypadku 4 razy na sekundę, czyli co 250 milisekund.

Zegar już tyka, napiszmy więc nową funkcję.

void OnTimer()
{
 
Przycisku_badz_czujny();
 
}
Kod MQL4

Dobra robota. Właśnie stworzyliście algorytm odpowiedzialny za zabezpieczenie naszego przycisku przed przypadkowym kliknięciem. To była ta trudna część. Od teraz, aby kliknąć w przycisk wymagane będzie przytrzymanie klawisza [ Shift ] na klawiaturze. Spiny na elektronach są odkręcane w funkcji Przycisku_badz_czujny(), którą zaimportowaliśmy z biblioteki Biblioteka_panic_button.ex4 w zeszłym tygodniu.

Platforma wymaga, aby uruchomiony timer wyłączyć, gdy nie będzie już nam potrzebny. Możemy to zrobić dodając funkcję API EventKillTimer() do funkcji OnDeinit(), którą utworzyliśmy w poprzedniej części i która, dla przypomnienia, będzie wywołana przez platformę przy zamknięciu naszej aplikacji. Zmodyfikujmy funkcję OnDeinit(), aby wyglądała tak jak funkcja poniżej.

void OnDeinit(const int reason)
{
 
Przycisku_bye_bye();
 
EventKillTimer();
}
Kod MQL4

Możecie w tym miejscu uruchomić aplikację i przekonać się jak algorytm reaguje na wciśnięcie klawisza [ Shift ].

Obserwujemy każdy ruch

Nadszedł czas, aby zacząć myśleć o funkcji zamykającej pozycje. W tym celu musimy rozpoznać moment samego kliknięcia w przycisk. Z pomocą przychodzi nam funkcja API void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam).

Funkcja OnChartEvent(…) jest wywoływana przez platformę zawsze, gdy na wykresie wystąpi zdarzenie, które może być istotne dla działania programu, na przykład użytkownik kliknie w obiekt lub ruszy myszką. Napiszmy więc odpowiednią funkcję.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
 
}
Kod MQL4

Platforma, wywołując funkcję OnChartEvent(…) przekazuje do niej dodatkowe informacje za pomocą parametrów id, lparam, dparamoraz sparam. Pozwala nam to zidentyfikować jakie zdarzenie miało miejsce, a typ zdarzenia zapisany jest w zmiennej id, która jest typu int, czyli jest liczbą całkowitą. Słówko const przy typie zmiennej oznacza, że danej zmiennej nie da się modyfikować i służy jedynie do odczytu informacji.

Rozwińmy naszą funkcję o kolejny element.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
 
if(id == CHARTEVENT_OBJECT_CLICK)
{
 
}
 
}
Kod MQL4

Za pomocą operatora if powyżej sprawdzamy czy zmienna id jest równa == wartości zdefiniowanej w zmiennej wygenerowanej automatycznie o nazwie CHARTEVENT_OBJECT_CLICK. Jeśli tak to mamy pewność, że użytkownik kliknął w, jeszcze nie określony, obiekt na wykresie, na którym działa nasz algorytm.

Wiemy już, że gdy wyrażenie warunkowe operatora if okaże się logicznie prawdziwe to wykonany zostanie kod pomiędzy nawiasami klamrowymi { } należącymi do tego operatora. Dopiszmy więc do naszego kodu co powinno się wydarzyć, gdy na wykresie wystąpi kliknięcie w dowolny obiekt.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
 
if(id == CHARTEVENT_OBJECT_CLICK)
{
 
if(Przycisku_czy_jestes_klikniety(sparam) == true)
{
 
}
 
}
 
}
Kod MQL4

Kod powyżej sprawdza czy to właśnie nasz przycisk został kliknięty korzystając z funkcji bool Przycisku_czy_jestes_klikniety(string nazwa_obiektu), która pochodzi z zaimportowanej biblioteki. Funkcja jako parametr przyjmuje zmienną o typie danych string, czyli zmienną tekstową, która powinna zawierać nazwę obiektu, który został kliknięty przez użytkownika. Funkcja porówna otrzymaną wartość z nazwą elementu graficznego przycisku i na pytanie czy to nasz przycisk został kliknięty odpowie wartością typu bool, czyli wartością true lub false.

Skąd znamy nazwę obiektu, który został kliknięty? Funkcja OnChartEvent(…) otrzymuje od platformy nazwę ostatnio klikniętego obiektu za pomocą parametru string o nazwie sparam, który możemy bezpośrednio przekazać jako parametr dla naszej funkcji, o tak Przycisku_czy_jestes_klikniety(sparam).

Realizujemy powierzone zadanie [Konkrety]

Gdy funkcjaPrzycisku_czy_jestes_klikniety(…) zwróci wartość true będziemy wiedzieć, że nie ma żartów, trzeba ratować sytuację.

Dopiszmy do naszego kodu funkcję, która zrealizuje główne zadanie naszego algorytmu, czyli zamknie pozycje na koncie. Funkcja posiada następującą deklarację:

bool Zamknij_pozycje_i_zlecenia(bool tylko_ten_instrument),

W praktyce umieszczamy ją w kodzie tak jak w ramce poniżej.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
 
if(id == CHARTEVENT_OBJECT_CLICK)
{
 
if(Przycisku_czy_jestes_klikniety(sparam) == true)
{
 
Zamknij_pozycje_i_zlecenia(Tylko_ten_instrument);
 
}
 
}
 
}
Kod MQL4

Funkcja Zamknij_pozycje_i_zlecenia(…) przyjmuje jako argument zmienną typu bool, która powinna nieść informację, true lub false, czy powinniśmy zamknąć pozycje tylko tego instrumentu, na którym pracuje algorytm. Pamiętacie parametr Tylko_ten_instrument z okienka ustawień w pierwszej części? Właśnie się nam przydał. W zależności od naszego wyboru w ustawieniach program zamknie pozycje, które są zgodne z wybranym wykresem lub wszystkie pozycje na koncie.


expert advisors


Tworzymy główną funkcję

Rozpoczynamy edycję pliku “Przydatne_funkcje.mqh” z katalogu MQL4\Include\Panic Button\

Do tej pory korzystaliśmy wyłącznie z funkcji, które pochodzą z MQL API lub są zaimportowane z biblioteki Biblioteka_panic_button.ex4. W prawdziwym świecie programowanie wymaga jednak, abyśmy sami tworzyli większość funkcji wspomagając się przy tym całym spektrum gotowych już rozwiązań. Funkcję Zamknij_pozycje_i_zlecenia(…) , z której korzystamy wyżej, znajdziecie w pliku Przydatne_funkcje.mqh, który w poprzedniej części umieściliśmy w katalogu MQL4\Include\Panic Button\ i dołączyliśmy do kodu za pomocą słowa kluczowego #include. Otwórzmy w MetaEditor wspomniany plik, ponieważ, niespodzianka, funkcja zamykająca pozycje wprawdzie znajduje się już w pliku, ale musimy jeszcze nadać jej treść.

Definicja funkcji

Po otwarciu pliku Przydatne_funkcje.mqh zobaczycie, że znajduje się w nim definicja funkcji zamykającej pozycje. Wygląda podobnie jak funkcje z których korzystaliśmy do tej pory z tą różnicą, że jest nasza, własna i możemy nadać jej jaką dusza zapragnie nazwę, typ i zbiór przyjmowanych parametrów. Wasza funkcja powinna wyglądać, mniej więcej, tak jak funkcja poniżej:

bool Zamknij_pozycje_i_zlecenia(bool tylko_ten_instrument)
{
return true;
}
Kod MQL4

Tym samym, w nagłówku , zdefiniowaliśmy funkcję o nazwie Zamknij_pozycje_i_zlecenia zwracającą typ danych bool i przyjmującą jeden parametr nazwany tylko_ten_instrument również o typie danych bool.

Treść funkcji

Wszystko co umieścimy pomiędzy nawiasami klamrowymi { } po nagłówku funkcji składa się na jej treść. Wzbogaćmy więc naszą treść o pierwszy kod.

bool Zamknij_pozycje_i_zlecenia(bool tylko_ten_instrument)
{
if(IsConnected() == false) return false;
int i = 0; int liczba_pozycji_i_zlecen = OrdersTotal();
if(liczba_pozycji_i_zlecen == 0) return true;
bool rezultat = true;
return rezultat;
}
Kod MQL4

Powyżej, pierwszym if, sprawdzamy czy mamy połączenie z serwerem za pomocą funkcji API IsConnected() i w przypadku problemów opuszczamy funkcję za pomocą operatora return zwracając jednocześnie wartość false, aby dać znać że coś jest nie tak. Zgadza się, po if nie ma nawiasów klamrowych, ponieważ korzystamy ze skróconego zapisu możliwego w sytuacjach kiedy po wyrażeniu warunkowym chcemy wykonać wyłącznie jedną operację.

Gdy jesteśmy online algorytm przechodzi dalej i tworzy dwie zmienne int o nazwach i oraz liczba_pozycji_i_zlecen przypisując do pierwszej wartość 0, a do drugiej wartość pochodzącą z funkcji API OrdersTotal(), która informuje o liczbie otwartych pozycji i zleceń na koncie.

Kolejny operator if sprawdza czy czasem wszystkie pozycje nie są już zamknięte, a użytkownik klika w przycisk jedynie dla zabawy lub w afekcie co skutkuje opuszczeniem funkcji z przekazaniem wartości true, bo właściwie wszystko jest ok.

Gdy funkcja wciąż ma co robić tworzy zmienną bool o nazwie rezultat, która posłuży nam później do przekazania informacji czy wszystkie pozycje zostały poprawnie zamknięte za pomocą końcowego operatora return, który nieco zmodyfikowaliśmy w tym kroku.

Pętla for

Operator for, który swoją popularnością ustępuje prawdopodobnie jedynie operatorowi if, służy do wykonania pewnego zbioru operacji określoną liczbę powtórzeń. Idealnie przydaje się w sytuacjach takich jak nasza, ponieważ musimy wykonać operację zamknięcia dla każdego ze zleceń osobno. Dodajmy do naszej funkcji pętlę for.

bool Zamknij_pozycje_i_zlecenia(bool tylko_ten_instrument)
{
if(IsConnected() == false) return false;
int i = 0; int liczba_pozycji_i_zlecen = OrdersTotal();
if(liczba_pozycji_i_zlecen == 0) return true;
bool rezultat = true;
for(i = liczba_pozycji_i_zlecen − 1; i >= 0; i−−)
{
 
}
return rezultat;
}
Kod MQL4

W nagłówku, po operatorze for widzimy tajemniczy zapis: (i = liczba_pozycji_i_zlecen – 1; i >= 0; i−−) .

Zapis oznacza, że pętla startuje z utworzoną przez nas wcześniej zmienną i równą zmiennej liczba_pozycji_i_zlecen pomniejszoną o jeden i będzie powtarzać operację zawartą między nawiasami klamrowymi { } tak długo jak zmienna i jest większa lub równa od zera [i >= 0], a po zakończeniu każdego cyklu zmniejszy zmienną i o jeden [i−−], tak aby pętla miała szansę się kiedyś skończyć. Proste, nieskomplikowane i wygodnie się czyta.

Mówiąc w języku ludzi pętla for wykona operację w nawiasach { } tyle razy ile mamy otwartych pozycji.

Dodajmy treść do naszej pętli.

bool Zamknij_pozycje_i_zlecenia(bool tylko_ten_instrument)
{
if(IsConnected() == false) return false;
int i = 0; int liczba_pozycji_i_zlecen = OrdersTotal();
if(liczba_pozycji_i_zlecen == 0) return true;
bool rezultat = true;
for(i = liczba_pozycji_i_zlecen − 1; i >= 0; i−−)
{
if(OrderSelect(i, SELECT_BY_POS) == false)
{
rezultat = false; continue;
}
if(tylko_ten_instrument == true && OrderSymbol() != Symbol()) continue;
if(OrderType() == OP_BUY || OrderType() == OP_SELL)
{
if(OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 0) == false)
{
rezultat = false; continue;
}
}
else if(OrderDelete(OrderTicket()) == false) rezultat = false;
}
return rezultat;
}
Kod MQL4

Fizyka rakietowa

Przeanalizujmy, krok po kroku, co dzieje się wewnątrz naszej pętli for, którą dzielnie napisaliśmy w poprzednim etapie.

W pierwszym kroku za pomocą funkcji API OrderSelect(…) wybieramy zlecenie z terminala korzystając z numeru jego pozycji na liście zleceń (pierwsze zlecenie na liście ma numer zero). Służy nam w tym celu zmienna i, która zmienia wartość z każdą pętlą. W przypadku niepowodzenia zapisujemy błąd w zmiennej rezultat i informujemy pętlę for za pomocą operatora continue, że powinna w tym miejscu przerwać i rozpocząć nowy cykl ze zmienną i pomniejszoną o jeden [i−−] .

Gdy wszystko przebiegnie w porządku kolejny operator if wykorzysta, nowość, operację logiczną “oraz” &&, która zwraca wartość true tylko i wyłącznie, gdy oba wyrażenia, po każdej z jej stron, zwrócą wartość true. Tym samym, gdy instrument wybranego zlecenia [OrderSymbol()] nie odpowiada [!=] instrumentowi wykresu [Symbol()], a zmienna tylko_ten_instrument posiada wartość true, ignorujemy dane zlecenie i za pomocą operatora continue przechodzimy do kolejnego cyklu pętli for. We wszystkich innych przypadkach algorytm przechodzi niżej.

Następnie nasz program sprawdza z jakim zleceniem mamy przyjemność, a robi to za pomocą funkcji API OrderType() i operacji logicznej “lub” ||, która zwraca wartość true, gdy przynajmniej jedno z wyrażeń po każdej z jej stron zwróci wartość true. W przeciwnym wypadku, gdy wyrażenie logiczne operatora if , jako całość, zwróci wartość false zostanie wykonany operator else if, który współpracuje z pierwszym if’em i służy mu jako plan B, kiedy ten nie może dojść do porozumienia z własnym wyrażeniem logicznym.

W zależności od typu zlecenia pierwszy if , gdy ma do czynienia ze zleceniem typu market buy [OP_BUY] lub market sell [OP_SELL] zamknie pozycję za pomocą funkcji API OrderClose(…), a drugi if, a właściwie else if anuluje zlecenia oczekujące korzystając z funkcji OrderDelete(…).

Chylę czoła, program jest gotowy!

Zapisujemy zmiany w pliku Przydatne_funkcje.mqh, wracamy do głównego kodu aplikacji Panic Button.mq4 i entuzjastycznie, pełni energii i ciekawości wynikającej z chęci przetestowania nowego dzieła wciskamy … Kompiluj! Następnie poprawiamy błędy i widzimy się w przyszłym tygodniu! 🙂

POBIERZ ZESTAW PLIKÓW MQL

Co o tym sądzisz?
Lubię
33%
Interesujące
67%
Heh...
0%
Szok!
0%
Nie lubię
0%
Szkoda
0%
O Autorze
Radek Szafron
Radek Szafron
Autorem publikacji jest Radek Szafron, właściciel firmy Expert Advisors, która od wielu lat wspiera inwestorów dostarczając technologie dedykowane dla rynku FOREX. Autor jest absolwentem finansów SGH ze specjalizacją "Rynki finansowe" i programistą z prawie 20 letnim doświadczeniem. Firma realizuje projekty algorytmów i aplikacji pisanych we wszystkich językach z rodziny "C", w tym dla popularnych platform Meta Trader 4 i 5. Firmę Expert Advisors można znaleźć pod adresem www.expertadvisors.pl.