Programowanie
Teraz czytasz
MQL Praktycznie. As Wywiadu, czyli powiadomienia na telefon [Kurs programowania]
0

MQL Praktycznie. As Wywiadu, czyli powiadomienia na telefon [Kurs programowania]

utworzył Radek Szafron12 kwietnia 2019

Witajcie ponownie, w IV już części naszego praktycznego kursu programowania. Dziś zajmiemy się sprawami niebagatelnej wagi, stworzymy Asa Wywiadu, który pod naszą nieobecność zajmie się działalnością wywiadowczą skupioną na zmianach cen na giełdzie. Gdy sprawy przybiorą odpowiedniego tempa, nasz As niezwłocznie wyśle na nasz telefon komórkowy tajną (nie bardzo) wiadomość, która pozwoli nam przejąc sprawy we własne ręce.

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

Nasza aplikacja umożliwi określenie poziomów cen, po osiągnięciu których otrzymamy powiadomienie push na nasz telefon. Poziomy będziemy ustalać za pomocą przeciągania myszką kolorowych linii, które narysują się po kliknięciu w narysowaną na wykresie postać naszego Asa Wywiadu. Zacznijmy więc o tego czym są wspomniane powiadomienia i jak należy skonfigurować nasze urządzenia by wszystko było ok.

Powiadomienia push

Powiadomienia push są funkcją platformy Meta Trader, która pozwala wysyłać wiadomości z terminala do urządzenia iOS lub Android, które posiada zainstalowaną mobilną aplikację Meta Trader. Jeśli jeszcze nie posiadacie aplikacji na swojej komórce pobierzcie ją proszę, ponieważ bez niej nasz As nie poradzi sobie zbyt dobrze.

rys_1 rys_2

Konfiguracja urządzeń jest bardzo szybka. W pierwszym kroku należy poznać swój MetaQuotes ID, czyli numer za pomocą którego będziemy kierować wiadomości do odpowiedniego telefonu (lub lodówki z systemem android). Numer MetaQuotes ID znajduje się w aplikacji mobilnej w menu Ustawienia/Czat i Wiadomości, tak jak na obrazku poniżej.

rys_3

Oczytany numer MetaQuotes ID należy następnie wprowadzić w programie Meta Tarder na naszym komputerze w menu Narzędzia/ Opcje/ Powiadomienia.

rys_4

Gotowe!

Zaplecze techniczne

Na początek pobierzcie paczkę zamieszczoną nieco niżej i zainstalujcie kod źródłowy dzisiejszej aplikacji, czyli plik As Wywiadu.mq4 w katalogu MQL4\Experts\ .

Nasz program będzie posiadał również niewielki interfejs graficzny, umieszczony w bibliotece elementy_graficzne.ex4, którą skopiujcie do katalogu MQL4\Libraries\As Wywiadu\ .

POBIERZ PLIKI

Startujemy

Po otwarciu pliku As Wywiadu.mq4, w jego górnej części, zobaczycie poniższy fragment kodu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#property strict
#import “As Wywiadu\\elementy_graficzne.ex4”
void AgencieRozpocznijMisje(int x, int y);
void AgencieByeBye();
string AgencieIdentyfikujSie();
#import
input color Kolor = clrDodgerBlue;
input color Kolor_wylaczony = clrGold;
input ENUM_LINE_STYLE Styl = STYLE_SOLID;
input int Grubosc = 2;
string Nazwa_instrumentu = “”;
string Id_linii = “LINIE_WYWIADU”;
Kod MQL4

Kod rozpoczynamy od importu biblioteki, a następnie za pomocą słów kluczowych input tworzymy parametry do konfiguracji w oknie ustawień. Ustawienia pozwolą nam określić wygląd linii, za pomocą których będziemy ustawiać cenę dla naszych powiadomień.

Zmienne globalne

Pod definicjami typu input widzimy coś nowego, deklaracje zmiennych Nazwa_instrumentu oraz Id_linii, które znajdują się poza treścią jakiejkolwiek funkcji. Co to oznacza? W ten sposób definiujemy zmienne globalne, które będą dostępne w każdym miejscu w naszym kodzie, czyli każda funkcja algorytmu będzie mogła je odczytywać i nadawać im wartość. Od zmiennych deklarowanych jako input różnią się jedynie tym, że użytkownik nie ma do nich dostępu w oknie ustawień.

Tablice

Podczas pisania oprogramowania bardzo często pojawia się konieczność obsługi na tyle dużej liczby danych, że tworzenie odrębnej zmiennej dla każdej z nich byłoby, co najmniej, kłopotliwe. Na ratunek przychodzą tablice, które pozwalają utworzyć uporządkowany zbiór danych danego typu, np. typu double, dostępny za pomocą jednej, ustalonej przez nas nazwy. Dla każdej tablicy możemy (nie musimy) zdefiniować jej rozmiar, czyli ile sztuk danych danego typu będzie się w niej znajdować. Rzućmy okiem na poniższy fragment kodu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
string Tablica_nazwy_linii[];
double Tablica_dane_linii[][4];
#define CZAS_AKTYWACJI 0
#define TYP_SYGNALU 1
#define STATUS_LINII 2
#define CENA_LINII 3
#define SYGNAL_LONG 0
#define SYGNAL_SHORT 1
#define LINIA_WYLACZONA 0
#define LINIA_ZMODYFIKOWANA 1
#define LINIA_AKTYWNA 2
#define LINIA_USUNIETA 3
Kod MQL4

Tablice jednowymiarowe

W powyższym kodzie, w linijkach 1 i 2, stworzyliśmy dwie tablice. Tablica_nazwy_linii posłuży nam do przechowywania nazw obiektów, które będziemy rysować na wykresie. Każda linia będzie reprezentowała jedno, ustawione przez użytkownika, powiadomienie, a ponieważ nasza aplikacja umożliwi ustalenie dowolnej liczby powiadomień zaistniała potrzeba wykorzystania tablic.

Tablica_nazwy_linii jest tablicą jednowymiarową, co poznajemy po jednej parze nawiasów kwadratowych [ ]. W nawiasie kwadratowym możemy wpisać liczbę sztuk danych jakie chcemy umieścić w tablicy, ale w naszym przypadku nie wiemy ile na wykresie znajdować będzie się linii, ponieważ zależy to od użytkownika, więc pozostawiamy nawias pusty, co oznacza, że będziemy dynamicznie, na bieżąco, ustalać i zmieniać rozmiar tablicy w zależności od potrzeb. Rzućmy okiem na poniższy rysunek, który opisuje jak działają tablice jednowymiarowe.

rys_5

Tablice wielowymiarowe

Druga tablica, o nazwie Tablica_dane_linii posłuży nam do przechowywania dodatkowych danych dla każdego z ustalonych przez użytkownika powiadomień. Nie możemy umieścić dodatkowych informacji w tej samej tablicy co nazwy obiektów, ponieważ nazwy są typu string, a dane typu double, a jedna tablica może zawierać tylko jeden typ danych.

Tablica_dane_linii jest tablicą o dwóch wymiarach co symbolizują dwa nawiasy kwadratowe [][4]. Pierwszy wymiar posłuży do identyfikacji, tak jak w tablicy Tablica_nazwy_linii, którą linię mamy na myśli, a drugi wymiar do przechowywania 4 dodatkowych danych dla każdego z ustawionych powiadomień.

Aby było nam łatwiej poruszać się po elementach tablicy dwuwymiarowej, za pomocą słów kluczowych #define utworzyliśmy listę predefiniowanych wartości o, w miarę, wygodnych do zapamiętania nazwach. Pod każdą z nazw wymienionych po słowie kluczowym #define kryje się liczba, co do której mamy pewność, że będzie stała.

W dalszej części wykorzystamy tablice w praktyce, a tymczasem spójrzmy na poniższą grafikę, która tłumaczy jak działa tablica dwuwymiarowa.

rys_6

Struktura aplikacji

Nasza aplikacja będzie wykorzystywała pięć, w większości znanych nam już, funkcji MQL API do obsługi zdarzeń.

OnInit() – przygotowanie algorytmu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int OnInit()
{
if(IsDemo() == false) return INIT_FAILED;
//Zapis !ZbadajTeren() to skrócona wersja
//ZbadajTeren() == false
if(!ZbadajTeren()) ZatrzyjSlady();
EventSetMillisecondTimer(250);
AgencieRozpocznijMisje(0, 0);
return INIT_SUCCEEDED;
}
Kod MQL4

W funkcji OnInit() zaczynamy od sprawdzenia, czy bawimy się w odpowiednim miejscu, a następnie czy dane, które posiada aplikacja są poprawne. Jak to możliwe, skoro OnInit() to pierwsza wywoływana przez program funkcja, aby algorytm posiadał niepoprawne dane? Może do tego dojść np. gdy uruchomimy algorytm na wykresie jednego instrumentu, a następnie zmienimy symbol. W takiej sytuacji narysowane wcześniej linie będę musiały być usunięte, a wszystkie powiadomienia anulowane. Tym właśnie zajmuje się funkcja ZatrzyjSlady().

W kolejnym kroku uruchamiamy timer i wywołujemy z biblioteki funkcję AgencieRozpocznijMisje(…), której zadaniem jest narysowanie naszego Asa na wykresie.

W kodzie źródłowym znajdziecie jeszcze kilka innych, ciekawych funkcji. Dla tych z Was, którzy chcieliby prześwietlić naszego Asa od A do Z interpretacja dodatkowych materiałów powinna być dobrym treningiem w poruszaniu się po dokumentacji języka MQL. Cały kod dzisiejszej aplikacji starałem utrzymać się w ramach poruszonych zagadnień, tak, aby jego interpretacja była tak łatwa jak tylko to możliwe. Życzę owocnej nauki i, ponieważ znacie już sporo elementów języka, dobrej zabawy w tworzeniu pierwszych, samodzielnych rozwiązań!

OnChartEvent(…) – monitorowanie zdarzeń

1
2
3
4
5
6
7
8
9
10
11
12
void OnChartEvent(const int id, const long& lparam, const double& dparam,
const string& sparam)
{
if(id == CHARTEVENT_OBJECT_DRAG || id == CHARTEVENT_OBJECT_CHANGE)
{
if(StringFind(sparam, Id_linii) >= 0) AktualizujDane(sparam);
}
else if(id == CHARTEVENT_OBJECT_CLICK)
{
if(sparam == AgencieIdentyfikujSie()) UtworzLinie();
}
}
Kod MQL4

Dzięki funkcji OnChartEvent(…) monitorujemy interakcję użytkownika z aplikacją. W pierwszym if sprawdzamy, czy użytkownik przeciągnął na wykresie linię naszego powiadomienia (lub zmienił jej parametry w okienku ustawień) i jeśli tak się stało to aktualizujemy dane dotyczące naszych powiadomień w funkcji AktualizujDane(…), która jako parametr przyjmuje nazwę zmodyfikowanej przez użytkownika linii. Za pomocą otrzymanej nazwy linii funkcja lokalizuje odpowiedni wiersz w tablicach i aktualizuje odpowiednie dane m.in. informacje o ustalonej dla powiadomienia cenie.

W drugiej części funkcji monitorujemy zdarzenia klikania w obiekty na wykresie naszego algorytmu. Każde kliknięcie w narysowaną przez nas postać Asa Wywiadu powoduje utworzenie nowej linii na wykresie. Utworzone linie, po przesunięciu, wywołają funkcję AktualizujDane(…), która zaplanuje określone powiadomienie w zależności od położenia linii względem ceny w momencie interakcji z użytkownikiem.


expert advisors


OnTimer() – czynności cykliczne

1
2
3
4
void OnTimer()
{
SynchronizujDane();
}
Kod MQL4

Timer, który jest wywoływany 4 razy na sekundę, co zdefiniowaliśmy w funkcji OnInit(), synchronizuje dane w obrębie naszego algorytmu. Do zadań funkcji SynchronizujDane() należą, sprawdzanie, które linie zostały już usunięte oraz nadawanie im odpowiedniego koloru w zależności od tego, czy dane powiadomienie jest włączone.

OnTick() – obserwacja ceny

Wszystkie wymienione do tej pory funkcje MQL API były nam znajome. Czas na coś nowego. Funkcja OnTick() wywoływana jest przez platformę przy każdej zmianie ceny instrumentu, na którym działa algorytm. Jest to fundamentalna funkcja dla wielu strategii automatycznych. Spójrzmy na poniższy kod.

1
2
3
4
void OnTick()
{
PoczynWywiad();
}
Kod MQL4

W funkcji OnTick() nasz As bierze się do pracy i sprawdza czy cena instrumentu osiągnęła wartość wskazaną przez użytkownika. Przejdziemy do funkcji PoczynWywiad() nieco dalej, a teraz przeanalizujmy jeszcze jedną, ostatnią potrzebną nam funkcję do obsługi zdarzeń, OnDeinit(…).

OnDeinit(…) – zakończenie algorytmu

1
2
3
4
5
6
7
8
9
void OnDeinit(const int reason)
{
EventKillTimer();
AgencieByeBye();
if(reason != REASON_CHARTCHANGE && reason != REASON_PARAMETERS)
ZatrzyjSlady();
}
Kod MQL4

Na początek zatrzymujemy timer i kasujemy narysowany obrazek naszego agenta, a następnie, za pomocą operatora if sprawdzamy jaki jest powód zakończenia aplikacji. W pewnych przypadkach po wywołaniu funkcji OnDeinit(…) program włączy się ponownie i zacznie swój algorytm od początku, czyli od funkcji OnInit(). Dzieje się tak np. gdy użytkownik zmienia interwał wykresu. W takiej sytuacji, jeśli nie zmienił się instrument, nie chcemy skasować ustawionych przez nas powiadomień. Sprawdzając powód wywołania funkcji OnDeinit(…), czyli parametr reason, możemy zdecydować, czy należy usunąć obiekty i dane, czy też pozostawić ustawione powiadomienia bez zmian.

Tablice w praktyce

Zobaczmy jak nasza aplikacja wykorzystuje utworzone wcześniej tablice na przykładzie funkcji ZapiszDane(…), której zadaniem jest utworzenie w tablicach nowego wiersza na dane dla nowego powiadomienia.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool ZapiszDane(string nazwa_linii, double cena_linii)
{
int liczba_linii = ArraySize(Tablica_nazwy_linii);
ArrayResize(Tablica_nazwy_linii, liczba_linii + 1);
ArrayResize(Tablica_dane_linii, liczba_linii + 1);
//Zapis liczba_linii++ to skrócona wersja liczba_linii = liczba_linii + 1
liczba_linii++;
Tablica_nazwy_linii[liczba_linii – 1] = nazwa_linii;
Tablica_dane_linii[liczba_linii – 1][CZAS_AKTYWACJI] = 0;
Tablica_dane_linii[liczba_linii – 1][TYP_SYGNALU] = SYGNAL_LONG;
Tablica_dane_linii[liczba_linii – 1][STATUS_LINII] = LINIA_WYLACZONA;
Tablica_dane_linii[liczba_linii – 1][CENA_LINII] = cena_linii;
return true;
}
Kod MQL4

W linijce nr 3 sprawdzamy ile elementów zawiera Tablica_nazwy_linii, a ponieważ Tablica_nazwy_linii jest tablicą jednowymiarową liczba jej elementów powinna odpowiadać liczbie wierszy w obu tablicach. Następnie w linijkach nr 5 i 6 powiększamy liczbę wierszy naszych tablic o 1 za pomocą funkcji API ArrayResize(…). Nowy wiersz zostanie dodany na końcu każdej z tablic.

Zwiększamy zmienną liczba_linii o 1, ponieważ dodaliśmy jeden wiersz do tablic, a następnie uzupełniamy dane na temat nowego powiadomienia zaczynając od linijki nr 11, gdzie przypisujemy nazwę linii do odpowiedniego wiersza w tablicy Tablica_nazwy_linii. Pamiętajmy, że wiersze (i kolumny) w tablicach numerowane są od zera, zatem, gdy chcemy, przykładowo, zapisać informację dla piątego powiadomienia musimy użyć wiersza nr 4, stąd w nawiasie kwadratowym, po nazwie tablicy widzimy liczba_linii – 1 jako nr wiersza.

Do numeracji kolumn w tablicy Tablica_nazwy_linii wykorzystujemy wartości predefiniowane na początku naszego algorytmu za pomocą słów kluczowych #define. Pod nazwami wartości kryją się liczby od 0 do 3, ponieważ tablica posiada 4 kolumny. Dzięki wykorzystaniu tablicy dwuwymiarowej możemy dla każdego powiadomienia zapisać 4 dodatkowe informacje, m. in. cenę i typ sygnału, którym w funkcji ZapiszDane(…) nadajemy wartości początkowe.

As w akcji

Inną funkcją, która w naszym algorytmie wykorzystuje tablice jest funkcja PoczynWywiad(), gdzie odczytujemy dane przygotowane w tablicach i interpretujemy je w celu podjęcia decyzji, czy należy wysłać do użytkownika wiadomość. Spójrzmy na kod funkcji PoczynWywiad().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void PoczynWywiad()
{
int i = 0;
int liczba_linii = ArraySize(Tablica_nazwy_linii);
for(i = 0; i < liczba_linii; i++)
{
if(Tablica_dane_linii[i][STATUS_LINII] == LINIA_AKTYWNA)
{
if(Tablica_dane_linii[i][TYP_SYGNALU] == SYGNAL_LONG)
{
if(Bid >= Tablica_dane_linii[i][CENA_LINII])
{
WyslijWiadomosc(i);
Tablica_dane_linii[i][STATUS_LINII] = LINIA_WYLACZONA;
}
}
else
{
if(Bid <= Tablica_dane_linii[i][CENA_LINII])
{
WyslijWiadomosc(i);
Tablica_dane_linii[i][STATUS_LINII] = LINIA_WYLACZONA;
}
}
}
}
}

W powyższej funkcji, za pomocą pętli for uzyskujemy dostęp do każdego wiersza w tablicy Tablica_dane_linii. Pozwala nam to na sprawdzenie statusu powiadomienia oraz typu sygnału, a na podstawie otrzymanych danych na zweryfikowanie czy cena Bid spełnia oczekiwany przez nas warunek. Gdy nasz As uzna, że trzymanie nas w niewiedzy byłoby nieroztropne, śpiesznie wysyła do nas powiadomienie korzystając z funkcji WyslijWiadomosc(…), której sprytnie podaje nr wiersza, w którym zapisane są dane na temat potrzebnego powiadomienia. Po wysyłaniu wiadomości dane powiadomienie zostaje wyłączone, a As, niezłomnie, kontynuuje dalej.

Rućcie okiem w kodzie na Waszym komputerze na funkcję WyslijWiadomosc(…), aby zobaczyć jak nasz algorytm wykorzystuje funkcję MQL API SendNotification(…) przy wysyłaniu powiadomień push.

Podsumowanie

Jeśli platforma nie zrobiła jeszcze tego za Was skompilujcie plik As Wywiadu.mq4 i przetestujcie Waszą nową aplikację! Aby narysować nową linię wystarczy kliknąć w sympatyczny obrazek na wykresie.

W kodzie źródłowym znajdziecie jeszcze kilka innych, ciekawych funkcji. Dla tych z Was, którzy chcieliby prześwietlić naszego Asa od A do Z interpretacja dodatkowych materiałów powinna być dobrym treningiem w poruszaniu się po dokumentacji języka MQL. Cały kod dzisiejszej aplikacji starałem utrzymać się w ramach poruszonych zagadnień, tak, aby jego interpretacja była tak łatwa jak tylko to możliwe. Życzę owocnej nauki i, ponieważ znacie już sporo elementów języka, dobrej zabawy w tworzeniu pierwszych, samodzielnych rozwiązań!

Co o tym sądzisz?
Lubię
20%
Interesujące
80%
Heh...
0%
Co?
0%
Nie lubię
0%
Tragedia
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.