Programowanie
Teraz czytasz
MQL Praktycznie. Algo Trader cz. I [Kurs programowania]
0

MQL Praktycznie. Algo Trader cz. I [Kurs programowania]

utworzył Radek Szafron4 lipca 2019

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

algotrader

Interfejs

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.

PRZECZYTAJ ARTYKUŁ

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.

class cChartOperations { public: cChartOperations(int width, int height) { //… } void OnClick(int x, int y) { //… } void OnChartChange() { //… } private: int Chart_width; int Chart_height; uint Last_click; int Last_click_x, Last_click_y; bool IsDoubleClick(int x, int y) { //… } };

 

 

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:

cChartOperations(int width, int height) { }

 

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.


expert advisors


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():

cChartOperations* ChartOperations = NULL; int OnInit() { //(…) int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); int chart_height = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS); ChartOperations = new cChartOperations(chart_width, chart_height); return(INIT_SUCCEEDED); }

Pointer

W części pliku odpowiedzialnej za definicję zmiennych globalnych za pomocą fragmentu kodu …

cChartOperations* ChartOperations = NULL;

 

… 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.

ChartOperations = new cChartOperations(chart_width, chart_height);

 

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.

void OnDeinit(const int reason) { //(…) delete ChartOperations; }

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:

void OnClick(int x, int y) { if(IsDoubleClick(x, y)) GoToPrice(x, y); }

 

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(…)

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //(…) if(id == CHARTEVENT_CLICK) ChartOperations.OnClick((int)lparam, (int)dparam); }

 

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.

programowanie mql

Myszka i poziomy cen

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

class cOrderOperations { public: cOrderOperations(int position_id) { Position_id = position_id; } void OnTimer200ms() { ProcessOrderRequests(); } private: int Position_id; void ProcessOrderRequests() { sOrderRequest request = ReadOrderRequest(); if(request.Request_id <= 0) return; if(request.Order_symbol != Symbol() || request.Position_id != Position_id) { request.Request_status = ORDER_REQUEST_STATUS_FAILED; request.Request_error = ORDER_REQUEST_ERROR_WRONG_POSITION; return; } if(OrderSend(request.Order_symbol, request.Order_type, request.Order_volume, request.Order_price, 0, 0, 0, request.Order_comment, request.Position_id) > 0) { request.Request_status = ORDER_REQUEST_STATUS_COMPLETED; } else { request.Request_status = ORDER_REQUEST_STATUS_FAILED; request.Request_error = _LastError; } UpdateOrderRequest(request); } };

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.

void ProcessOrderRequests() { sOrderRequest request = ReadOrderRequest(); //(…) }

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.

struct sOrderRequest { int Request_id; int Position_id; int Order_type; double Order_volume; double Order_price; double Order_range; string Order_symbol; string Order_comment; int Request_status; int Request_error; };

 

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().

void ProcessOrderRequests() { sOrderRequest request = 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.

if(OrderSend(request.Order_symbol, request.Order_type, request.Order_volume, request.Order_price, 0, 0, 0, request.Order_comment, request.Position_id) > 0) { request.Request_status = ORDER_REQUEST_STATUS_COMPLETED; }

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().

input int Position_id = 8821; //Unikalny identyfikator pozycji cOrderOperations* OrderOperations = NULL; int OnInit() { EventSetMillisecondTimer(200); //(…) OrderOperations = new cOrderOperations(Position_id); return(INIT_SUCCEEDED); }

 

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.

void OnTimer() { //(…) OrderOperations.OnTimer200ms(); }

 

Funkcja obiektu OnTimer200ms() wywoła funkcję ProcessOrderRequests(), która złoży zlecenie, gdy otrzyma z interfejsu odpowiednie dane.

class cOrderOperations { public: cOrderOperations(int position_id) { Position_id = position_id; } void OnTimer200ms() { ProcessOrderRequests(); } //(…) };

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

wolumen forex

Obsługa wolumenu

Rodzaje zleceń

algo trading

Obsługa zleceń

Podsumowanie

Na dziś to już wszystko, ale to dopiero początek przygody z Algo Traderem. 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 .

Co o tym sądzisz?
Lubię
63%
Interesujące
25%
Heh...
13%
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.