MQL Praktycznie. Algo Trader cz. I [Kurs programowania]
W dzisiejszym odcinku rozpoczniemy pracę nad oprogramowaniem, które pozwoli Wam wykorzystać możliwości jakie niesie ze sobą handel algorytmiczny przy jednoczesnym zachowaniu maksimum kontroli nad przebiegiem zdarzeń. Skupimy się na pomysłach, które pozwalają zaangażować się w rzeczywisty trading poprzez maksymalizację intuicyjności obsługi oraz elastyczności funkcji. W oprogramowaniu wykorzystamy interfejs, którego forma jest przez wielu uważana za standard w branży day tradingu i nazywana jest często price ladder.
Plan projektu
Pracę podzielimy na kilka etapów, ponieważ poza szeroką obsługą zleceń, Algo Trader pozwoli Wam uruchamiać algorytmy, które będą automatycznie zarządzały pozycją według różnorodnych kombinacji, które będziecie mogli tworzyć w zależności od sytuacji jaką napotkacie na rynku.
W dzisiejszej części zrobimy pierwszy krok w świat programowania obiektowego i uruchomimy elementarną część naszego projektu związaną z interfejsem i obsługą podstawowych operacji.
W kolejnych odcinkach rozpoczniemy pracę nad pierwszymi algorytmami. Na początek stworzymy algorytm GTP, który będzie pilnował by pozycja zamknęła się z oczekiwanym zyskiem niezależnie od tego z ilu składa się zleceń. Dalej stworzymy algorytm OOO (One Opens the Other, tak dokładnie, takiego jeszcze nie było), a na koniec algorytm umożliwiający scalping podczas prezentacji ważnych danych makro. Razem z algorytmami, tu i ówdzie, dodamy jakiś fajny gadżet.
Zaplecze techniczne
Paczkę z potrzebnymi na dziś plikami znajdziecie po tym linkiem:
Korzystanie z oprogramowania oznacza akceptację licencji dołączonej do zestawu plików.
Aby oprogramowanie działało poprawnie potrzebne jest środowisko Microsoft NET Framework w wersji min. 4.6.1 Runtime, które można pobrać tutaj:
Programowanie obiektowe
Nasze oprogramowanie będziemy tworzyć wykorzystując programowanie obiektowe, czyli sposób programowania umożliwiający logiczne uporządkowanie fragmentów algorytmu w tzw. obiekty, których zadaniem jest realizowanie wyodrębnionych funkcji oraz komunikowanie się z innymi obiektami w celu realizacji zadania programu jako całości. Aby przejść dalej potrzebne będzie nam zrozumieć czym w praktyce są obiekty i jak się je tworzy. W tym celu rzućcie okiem na poniższy artykuł w Wikibooks, który na przykładzie języka C++, na którym bazuje MQL4, pozwala zrozumieć niezbędne podstawy.
Pierwsza klasa
Jak widzicie wszystko kręci się wokół słowa kluczowego class. Pozwala ono zdefiniować obiekt, który następnie możemy utworzyć za pomocą słowa kluczowego new. Otwórzcie plik zawierający definicję jednej z klas jakie będziemy dzisiaj wykorzystywać, plik ChartOperations.mqh, który znajdziecie w katalogu MQL4\Include\AlgoTrader_v10.
Każda klasa posiada konstruktor, który w praktyce jest funkcją, która jest automatycznie wywoływana w momencie utworzenia obiektu. Konstruktor nosi taką samą nazwę jak obiekt i może przyjmować parametry. W naszym powyższym fragmencie kodu, konstruktor tworzymy w następujący sposób:
Po utworzeniu konstruktora możemy definiować składniki klasy, czyli funkcje i zmienne. Definiuje się je dokładnie tak jak w przypadku zwykłych funkcji i zmiennych z tą różnicą, że w ciele klasy. Zwróćcie uwagę na słowa kluczowe public: oraz private:. Wszystkie funkcje i zmienne, które znajdują się po słowie public: będą dostępne dla funkcji, które znajdują się w innych częściach oprogramowania, np. w funkcji API OnTick() lub innych obiektach. Private natomiast informuje kompilator, że elementy, które są zdefiniowane w tej części są ściśle tajne i za żadne skarby nikt, poza danym obiektem naturalnie, nie może mieć do nich dostępu.
Zastosowania obiektów
Co tworzy obiekty tak przydatnymi? Między innymi to, że raz zdefiniowany obiekt możemy utworzyć w dowolnej ilości kopii i w zależności od przekazanych im parametrów będą się one zachowywały odpowiednio różnie, ale w sposób, który jesteśmy zdolni analizować i kontrolować. Ponadto obiekty pozwalają na czytelną organizację kodu poprzez rozłożenie części składowych algorytmu na wyodrębnione elementy, a odpowiednio zaprojektowany obiekt możemy wykorzystać w zupełnie innym oprogramowaniu, co pozwala efektywniej tworzyć złożone aplikacje.
Uruchamiamy pierwszy obiekt
Przejdźmy do głównego pliku naszego oprogramowania, pliku AlgoTrader_v10.mq4, który znajdziecie w katalogu MQL4\Experts\.
Rzućmy okiem na poniższą definicję funkcji API OnInit():
Pointer
W części pliku odpowiedzialnej za definicję zmiennych globalnych za pomocą fragmentu kodu …
… tworzymy tzw. pointer czyli wskaźnik, który będzie zawierał adres do miejsca w pamięci komputera, gdzie później zostanie utworzony nasz obiekt. Za pomocą wskaźnika będziemy mogli odwoływać się do obiektu i wywoływać jego funkcje. Pointery definiujemy podobnie do zmiennych, pierwszy element notacji to typ obiektu, następnie umieszczamy gwiazdkę * , po której ustalamy unikalną nazwę obiektu, którą później wykorzystamy do odwołań. Do naszego pointera przypisujemy zmienną NULL, która mówi, że na obecnym etapie nie wskazuje on jeszcze na żaden obiekt.
Konstruktor
W dalszej części kodu, w ciele funkcji OnInit() tworzymy właściwy obiekt za pomocą słowa kluczowego new.
Po new wywołujemy konstruktor obiektu, który w naszym przypadku przyjmuje dwie zmienne informujące obiekt o wymiarach naszego wykresu, ponieważ jest to nam potrzebne, aby prawidłowo ulokować nasz interfejs. Wywołany konstruktor przekazuje adres nowo utworzonego obiektu w pamięci komputera do naszego pointera o nazwie ChartOperations.
Destruktor
Obiekty są szczególnym typem danych, który jeśli utworzymy to musimy zadbać, aby na koniec został usunięty z pamięci komputera. Wszystko po to, aby w obok Waszego laptopa nie musiał stać wielki serwer zbierający wcześniej utworzone dane, które już nikomu nie są potrzebne.
W celu usuwania obiektów korzystamy ze słowa kluczowego delete, po którym umieszczamy nazwę naszego poinera wskazującego, który obiekt mamy na myśli. Nasz obiekt usuwamy dopiero w funkcji API OnDeinit(…), ponieważ potrzebujemy go przez cały cykl pracy aplikacji, aż do momentu wyłączenia oprogramowania.
Praca z obiektami
Zobaczmy teraz jak możemy wykorzystać nasz utworzony obiekt na potrzeby naszej aplikacji. W tym celu w ciele klasy cChartOperations zdefiniowaliśmy kilka funkcji. Spójrzmy na jedną z nich:
Funkcja OnClick(…) jest nam potrzebna, aby komunikować do naszego interfejsu jakie poziomy cen są dla nas obecnie interesujące. Poprzez dwukrotne kliknięcie na wykresie algorytm ustawi naszą drabinkę cen we wskazanym miejscu. Aby nasza funkcja mogła działać potrzebujemy wykryć moment, gdy klikamy w wykres, a tu z pomocą przychodzi funkcja API OnChartEvent(…)
Powyższy fragment kodu rozpoznaje moment, gdy użytkownik klika w wykres, a następnie wywołuje funkcję, która znajduje się wewnątrz naszego wcześniej utworzonego obiektu. Wywołanie funkcji polega na umieszczeniu kropki po nazwie naszego pointera do obiektu, a następnie przekazaniu nazwy interesującej nas funkcji wraz z potrzebnymi jej parametrami, które w naszym przypadku oznaczają współrzędne x i y miejsca na wykresie, w które nastąpiło kliknięcie.
Pierwsze efekty
Utworzone przez nas procedury uruchomiły nam pierwszą funkcjonalność oprogramowania, którą możemy przetestować kompilując nasz program i klikając w różne miejsca wykresie. Poniżej znajdziecie grafikę, która prezentuje klika funkcji, które pozwalają manipulować poziomami cen wyświetlonymi w naszym interfejsie graficznym.
Druga klasa
Skoro awansowaliście do drugiej klasy i aby nasze klikanie nie poszło na marne zaprojektujemy drugi obiekt, który pozwoli na składanie zleceń z poziomu naszej drabinki. W tym celu przygotowaliśmy odpowiednią klasę, którą znajdziecie w pliku MQL4\Include\AlgoTrader_v10\OrderOperations.mqh
W powyższym kodzie widzicie pełną definicję obiektu cOrderOperations, który będzie nam służył do obsługi zleceń, które będziemy składać na naszej drabince cen. Zwróćcie uwagę na funkcję ProcessOrderRequests(), w której nasz program komunikuje się z biblioteką dll za pomocą funkcji ReadOrderRequest().
Struktury
Funkcja ReadOrderRequest() jako wynik swojego działania zwraca strukturę typu sOrderRequest, która jest zdefiniowanym przez nas zbiorem danych o złożonym za pomocą interfejsu zleceniu.
Struktury to młodsze siostry klas, które pozwalają na stworzenie bloku danych zawierającego wiele zmiennych. Pozwala to na logiczne uporządkowanie informacji, które dotyczą danego zagadnienia. W naszym przypadku struktura gromadzi dane dotyczące zlecenia wysłanego przez nasz interfejs graficzny, takie jak cena otwarcia czy wolumen. Strukturę definiujemy poprzedzając jej nazwę słowem kluczowym struct, a następnie ustalamy listę potrzebnych nam danych.
Powyższą definicję znajdziecie w pliku MQL4\Include\AlgoTrader_v10\Definitions.mqh.
Ze struktur korzystamy podobnie jak ze zmiennych, czyli w pierwszym kroku musimy ją utworzyć określając typ struktury, a następnie nadać jej unikalną nazwę, do której możemy się później odwołać. W poniższym przykładzie tworzymy strukturę typu sOrderRequest i nadajemy jej unikalną nazwę request, a następnie przypisujemy jej dane pochodzące z naszego interfejsu za pomocą funkcji ReadOrderRequest().
Dostęp do danych zapisanych w strukturze jest analogiczny do korzystania ze zmiennych, ponieważ w praktyce struktura jest jedynie zbiorem zmiennych ukrytym pod wspólnym identyfikatorem. Aby odczytać lub zapisać dane w strukturze wystarczy po nazwie jaką jej nadaliśmy umieścić kropkę, a następnie wskazać nazwę interesującej nam zmiennej. W poniższym fragmencie kodu odczytujemy dane zapisane w strukturze, aby wysłać zlecenie, a następnie zapisujemy informacje o rezultacie operacji.
Realizacja zleceń
Aby nasze oprogramowanie mogło składać zlecenia musimy wcielić w życie obiekt utworzony na podstawie klasy cOrderOperations. Ponieważ obiekt będzie nam potrzebny już od uruchomienia naszego programu tworzymy go w funkcji API OnInit().
W konstruktorze cOrderOperations umieszczamy zmienną Position_id, która jest elementem ustawień naszej aplikacji dostępnych w domyślnym oknie platformy MetaTrader. Identyfikator pozwala naszemu oprogramowaniu rozpoznawać własne zlecenia, tak aby nie były pomieszanie ze zleceniami pochodzącymi z innych źródeł.
W kodzie powyżej uruchomiliśmy również Timer z interwałem 200 ms, ponieważ nasz obiekt powinien cyklicznie sprawdzać, czy interfejs nie wysyła informacji o nadchodzącym zleceniu.
Funkcja obiektu OnTimer200ms() wywoła funkcję ProcessOrderRequests(), która złoży zlecenie, gdy otrzyma z interfejsu odpowiednie dane.
Efekty
Nasze oprogramowanie potrafi już komunikować się z interfejsem graficznym i możemy zrobić to co lubimy robić najbardziej, czyli skompilować algorytm i złożyć nowe zlecenie. Aby ułatwić Wam pierwsze kroki poniżej znajdziecie kilka wskazówek, które obrazują jakie funkcje kryją się pod różnymi klawiszami.
Kontrola wolumenu
Rodzaje zleceń
Podsumowanie
Na dziś to już wszystko, ale to dopiero początek przygody z Algo Trader. W kolejnym odcinku przejdziemy do implementacji zaawansowanych funkcji związanych z obsługą algorytmów. Algo Trader będzie powstawał razem z Wami i będziemy wdzięczni za wszelkie pomysły i uwagi, które mogą uczynić go dla Was lepszym. Chętnie wysłuchamy Was pod adresem biuro@expertadvisors.pl .