Zmienne

Każda zmienna musi być zadeklarowana przed jej użyciem, czyli musi być jej nadana unikalna nazwa w zakresie swojej widoczności w programie oraz określony typ wartości, jaki będzie przechowywała.

Podstawowe typy zmiennych to:

bool - wartość logiczna prawdy (true) i fałsz (false);
string - łańcuchy tekstowy, znaków;
double – liczba zmiennoprzecinkowa podwójne precyzji

Przykład:

string TekstWiadomosci;
int statusZlecenia;
double AktualnaCena;
bool kontoDemo

Dodatkowe typy danych o typie podstawowym integer:

  • color – wartość całkowita reprezentująca kolory RGB;
  • datetime - data i godzina, typ całkowity bez znaku, zawiera liczbę sekund, które upłynęły od godziny 0:00 w dniu 1 stycznia 1970;

Dodatkowe typy danych stosuje się dla zachowania czytelniejszego zapisu, w szczególności przy przypisaniu wartości zmiennej.

Przykład:

datetime czas_rozpoczecia = D'2004.01.01 00:00;
color kolor_czcionki = C'0x44, 0xB9, 0xE6';

Tablicowy typ danych umożliwia zapisanie danych tego samego typu w tablicy o określonej długości. Tablice możemy zadeklarować, jako jednowymiarową lub wielowymiarową maksymalnie do czterech wymiarów. Numeracja elementów tablicy zaczyna się od 0, a ostatnim elementem tablicy jest jej zadeklarowana wielkość minus 1. Przykładowo definiując jednowymiarową tablicę 100 elementów typu int odwołujemy się do jej pierwszego elementu przez 0, a do ostatniego przez 99. To samo dotyczy tablic wielowymiarowych, czyli każdy wymiar indeksowany jest od 0 do wielkość tablicy minus 1.
W podczas pracy na tablicach należy zwrócić szczególną uwagę na prawidłowe posługiwanie się wielkością zadeklarowanej tablicy, tak aby nie odwoływać się po za zakres elementów tablicy. Informacje o błędnym odwoływaniu się do elementów, po za zakres indeksu możemy pobrać za pomocą funkcji GetLastError() wówczas otrzymamy kod błędu ERR_ARRAY_INDEX_OUT_OF_RANGE.

Przykład:

int a[100] // jednowymiarową tablicę 100 liczb całkowitych
double m[7][100] / / dwuwymiarowa tablica siedmiu tablic, każda z nich
składająca się z 100 liczb całkowitych razem otrzymujemy 700 elementów w
dwuwymiarowej tablicy

Zmienne globalne

Zmienne globalne są umieszczone w kodzie na tym samym poziomie, co wszystkie funkcje programu wówczas są widoczne i mogą być wykorzystane przez każdą z funkcji. Dotyczy to też funkcji specjalnych start(), init(), deinit(). Zmienne globalne są alokowane jednorazowo podczas pierwszego uruchomienia programu i są inicjowane wartościami zero, jeśli nie przypisano im wartości początkowych.

Zmienne globalne tak samo lokalne mogą być dowolnego typu dostępnego w MQL4, opisanego w poprzednim punkcie.
Uwaga! Zmiennej globalnej tu opisanej nie należy mylić i utożsamiać ze zmienną globalną terminala klienckiego, co zostało opisane w kolejnych punktach.

Przykład:

int ZmiennaGlobalna = 10 // zmienna globalna
int init ()
{
...
}
int start ()
{
Print(ZmiennaGlobalna);
}
double jakas_funkcja()
{
...
}

Zmienne lokalne

Zmienne lokalne w odróżnieniu od zmiennych globalnych deklarowane są zawsze wewnątrz bloku funkcji. Zakres widoczności i dostępności do zmiennej lokalnej ogranicza się do bloku funkcji, w którym została zadeklarowana i nie można odczytać jej wartości z poziomu innych funkcji. Nazwy zmiennych lokalnych mogą się powtarzać  w różnych funkcjach, a także mogą być takie same jak nazwy zmiennych globalnych, ale wówczas należy dokładnie analizować kod programu, aby nie popełnić błędów obliczeniowych przez operowanie na tych samych nazwach zmiennych lokalnych i globalnych. Najprosztszym rozwiązaniem zapewniającym przejrzystość kodu jest nie stosowanie tych samych nazw dla zmiennych lokalnych oraz globalnych.
Dopuszczalne typy zmiennych globalnych są takie same jak opisane powyżej typy zmiennych globalnych.

Przykład:

int kod_bledu()
{
int zwroc_kod = 0;
return(zwroc_kod);
}

Zmienne statyczne

Zmienne statyczne mogą być zarówno globalne jak i lokalne, a wyróżnia je zastosowanie przed definicją zmiennej słowa static. Zmienne statyczne stosujemy w bloku funkcji, a charakteryzuje je to, że zachowują swoją wartość podczas kolejnych wywołań funkcji.

Z punktu widzenia fizycznego przechowywania zmiennej statycznej w pamięci oznacza to, że jest przechowywana zawsze w tym samym miejscu pamięci fizycznej w odróżnieniu od zmiennych zwykłych, które mogą być alokowane dynamicznie podczas każdego wywołania funkcji, czyli dane w nich zawarte są tracone. Zmienne statyczne inicjowane są podczas wywołania funkcji init(), która nadaje im początkowe wartości zerowe, natomiast każde wywołanie funkcji, która zmodyfikuje zmienną statyczną powoduje, że ta zmienna cały czas jest przechowywana w pamięci i można ją pobrać, zmodyfikować przy kolejnych wywołaniach funkcji. Zmienną statyczną może być zmienna dowolnego typu, znajdująca się w bloku funkcji z wyjątkiem parametrów przekazywanych do funkcji.

Przykład:

// Deklaracja i definicja funkcji ze zmienną statyczną
int jakas_nazwa_funkcji()
{
static int sflaga = 10;
int iflag = 12;
return (bandery);
}

Zmienne zewnętrzne

W MQL4 zmienne zewnętrzne poprzedzane są słowem extern, co oznacza, że zmienna będzie widoczna, jako parametr EA, wskaźnika, skryptu i tylko do tego celu są wykorzystywane. Zmienne zewnętrzne mogą być tylko globalne i nie można stosować ich w bloku żadnej funkcji, a typ zmiennej zewnętrznej może być dowolny.

Przykład:

extern int ParametrSL = 50;
extern color ParametrChart = green;

int init ()
{
...
}
int start ()
{
...
}

Wartości początkowe zmiennych

Każda zmienna może być zainicjowana w momencie jej definiowania. Jeśli podczas definiowania zmiennej podamy tylko typ i nazwę przyjmie ona wartość domyślną 0 (zero), ale można nadać jej wartość początkową przez zwykłą operację przypisania. Można inicjować wartościami początkowymi zarówno zmienne globalne, lokalne jak i statyczne, zewnętrzne oraz parametry wywołania funkcji. Zmienne globalne są inicjowane tylko raz, natomiast zmienne lokalne są inicjowane za każdym razem wywołania funkcji, oczywiście z wyjątkiem zmiennych statycznych, które jak wiemy inicjowane są podczas wywołania funkcji init().

Wartościami początkowymi można też inicjować tablice, wówczas wartości początkowe muszą zostać umieszczone w nawiasach i być oddzielone przecinkami (,). Jeśli któryś z elementów tablicy nie zostanie zainicjowany to zostanie mu przypisana wartość 0 (zero). Jeśli początkowy rozmiar tablicy nie został określony to przyjmie ona taki rozmiar, jak ilóść wartości inicjalizacyjnych. Tablice wielowymiarowe są inicjowane podobnie jak jednowymiarowe przez ciągi wartości początkowych poczynając od pierwszego wymiaru, dlatego należy zwrócić uwagę, aby prawidłowo policzyć ciągi wartości określające poszczególne zmienne w każdym wymiarze tablicy.

Przykład:

int n = 1;
double p = MarketInfo(Symbol (), MODE_POINT);
string s = "Witaj";
double f[] = (0.0, 0.236, 0.382, 0.5, 0.618, 1.0);
int [4][4] = (1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4);

Wartości początkowe zmiennych

Każda zmienna może być zainicjowana w momencie jej definiowania. Jeśli podczas definiowania zmiennej podamy tylko typ i nazwę przyjmie ona wartość domyślną 0 (zero), ale można nadać jej wartość początkową przez zwykłą operację przypisania. Można inicjować wartościami początkowymi zarówno zmienne globalne, lokalne jak i statyczne, zewnętrzne oraz parametry wywołania funkcji. Zmienne globalne są inicjowane tylko raz, natomiast zmienne lokalne są inicjowane za każdym razem wywołania funkcji, oczywiście z wyjątkiem zmiennych statycznych, które jak wiemy inicjowane są podczas wywołania funkcji init().

Wartościami początkowymi można też inicjować tablice, wówczas wartości początkowe muszą zostać umieszczone w nawiasach i być oddzielone przecinkami (,). Jeśli któryś z elementów tablicy nie zostanie zainicjowany to zostanie mu przypisana wartość 0 (zero). Jeśli początkowy rozmiar tablicy nie został określony to przyjmie ona taki rozmiar, jak ilóść wartości inicjalizacyjnych. Tablice wielowymiarowe są inicjowane podobnie jak jednowymiarowe przez ciągi wartości początkowych poczynając od pierwszego wymiaru, dlatego należy zwrócić uwagę, aby prawidłowo policzyć ciągi wartości określające poszczególne zmienne w każdym wymiarze tablicy.

Przykład:

int n = 1;
double p = MarketInfo(Symbol (), MODE_POINT);
string s = "Witaj";
double f[] = (0.0, 0.236, 0.382, 0.5, 0.618, 1.0);
int [4][4] = (1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4);

Parametry wywołania funkcji

Parametry przekazywane do funkcji są lokalne. Zakres widoczności, dostępu do tych parametrów jest w zakresie bloku funkcji. Każdy parametr musi mieć swoją niepowtarzalną nazwę w zakresie bloku funkcji i nie może być taka sama jak nazwy zmiennych lokalnych tej funkcji.

Przykład:

void funkcja (int x[], double y, bool z)
{
if ( y > 0.0 && !z )
Print( x[0] );
}

Przekazywane parametry do funkcji mogą być inicjowane domyślnymi wartościami. Wyjątkiem są funkcje wywoływane pomiędzy modułami, gdzie nie można stosować parametrów domyślnych.

Przykład:

void funcja(int x, double y = 0.0, bool z = true)
{
if ( !z )
Print( y );
}
// możliwe sposoby wywołanie funkcji
funkcja(123);
funkcja(123, 0.1);
funkcja(123, 0.2, false);

Parametry mogą być przekazywane do funkcji przez wartość lub przez referencje. Przekazanie przez wartość oznacza, że funkcja nie ma możliwości modyfikacji tych parametrów w bloku wykonywalnym funkcji, w rzeczywistości funkcja pobiera kopie parametrów, natomiast przekazuje parametr przez referencje.

Funkcja może zmodyfikować ten parametr, inaczej mówiąc funkcja operuje na rzeczywistej zmiennej, a nie na jej kopi. Aby przekazać parametry przez referencje wystarczy poprzedzić je znakiem ampersand (&), ponieważ domyślnie parametry przekazywane są przez wartość. Możliwe jest przekazywanie tablice, jako parametrów funkcji.Przekazuje się jednak całe tablice i nie ma możliwości przekazywania pojedynczych elementów tablicy.

Parametry przekazywane przez referencję mogą być tylko w zakresie jednego modułu funkcjonalnego i nie mogą być przekazywane np. do funkcji znajdujących się w bibliotekach lub pomiędzy EA i wskaźnikiem. Przekazując parametry przez referencje nie możemy stosować wartości domyślnych.

Przykład:

// parameter przekazywane przez wartość
double funkcja(int x, double y, double z[])
{
double oblicz_tp;
for (int i=0; i < OrdersTotal(); i++)
{
if (i == ArraySize (z)) break;
if (OrderSelect(i) == false) break;
return ( OrderOpenPrice() );
}
}

// parametry przekazywane przez referencje
void funkcja(int & x, double & y, double & z[])
{
double oblicz_tp;
for (int i=0; i < OrdersTotal(); i++)
{
if (i == ArraySize (z)) break;
if (OrderSelect(i) == false) break;
z[i] = OrderOpenPrice();
}
x = i;
y = oblicz_tp;
}

Wywoływanie funkcji zewnętrznych

Bardzo przydatnym elementem języka MQL4 jest możliwość importowania/wywoływania funkcji zewnętrznych, co w tym przypadku oznacza możliwość wywołania funkcji z bibliotek w formacie EX4 lub DLL. Jak wiemy każda funkcja składa się z deklaracji (czyli tego jaka jest nazwa funkcji, jakie parametry przekazujemy do funkcji, jakie wartości są zwracane przez funkcję) oraz definicji (czyli bloku kodu wykonywalnego). W przypadku bibliotek zewnętrznych definicja czynności wykonywanych przez funkcję znajduje się w bibliotece zewnętrznej. Dlatego, aby skorzystać z funkcji zewnętrznych wystarczy tylko poinformować kompilator o tym, że będziemy importować funkcję stosujemy do tego słowo kluczowe #import z podaniem nazwy biblioteki o typie string, czyli podwójny cudzysłów, oraz podajemy dokładną deklarację funkcji.

Przykład:

# import "user32.dll"
int MessageBoxA(int hWnd, string szTekst, string szTytul, int nTyp);
int SendMessageA(int hWnd, int Msg, int wParam, int lParam);
# import "lib.ex4"
double round(double value);
# import

Parametry wywołania funkcji zewnętrznych mogą być przekazywane zarówno przez wartość jak i przez referencje, co w tym drugim przypadku oznacza, że przekazujemy tak zwany wskaźniki obszaru pamięci, który jest wspólnie dostępny dla kodu wykonywalnego funkcji jak i dla kodu MQL4. Dane przekazywane mogą być dowolnego typu, co jest jednoznaczne dla bibliotek EX4, natomiast może być problematyczne w przypadku bibliotek DLL, które mogą być napisane w dowolnym języku programowania wówczas konieczne może się okazać wykorzystanie rzutowania danych.

Dane typu string są przekazywane, jako wskaźnik (przez referencję) do odpowiedniego bloku pamięci (trzeba pamiętać, że wewnętrzna reprezentacja danych łańcuch składa się z dwóch części: długość bloku pamięci i wskaźnik do bloku pamięci). Jeśli istnieje potrzeba przekazywania danych typu int lub double przez referencje to przydatna może być do tego celu jednowymiarowa tablica odpowiedniego typu.

Przykład:

# import "twoja_biblioteka.dll"
void PrzekazIntPrzezReferencje(int & OneInt[]);
# import
int start ()
{
int tablica[1];
...
PrzekazIntPrzezReferencje(tablica);
Print(tablica[0]);
...
}

Preprocessor

Preprocessor jest to specjalny podprogram kompilatora MQL4, uruchamiany tylko podczas kompilacji kodu źródłowego mq4 do kodu wykonywalnego ex4. Z punktu widzenia tworzenia oprogramowania nie jest istotne co i jak robi preprocesor, dlatego proponuję tylko zapamiętać, że każde słowo poprzedzone znakiem chasz (#), jest prekompilowane przez preprocessor, a to co ma być zinterpretowane i wykonane nazywa się dyrektywą preprocesora. Składnia dyrektyw preprocessora zawsze zaczyna się od znaku chasz (#), który powinien być w pierwszej linii kodu, następnie jest dyrektywa i parametr lub dyrektywa parametr i wartość parametru, a kończy się zawsze znakiem nowej linii.

Dyrektywa #define

Dyrektywa #define służy do zdefiniowania stałej symbolicznej, która może przyjmować wartości podstawowych typów danych dostępnych w MQL4.
Typowym zastosowaniem tej dyrektywy jest zdefiniowanie stałej wartości np. stałego tekstu lub stałych wartości określających kody błędów, które podczas wywołania będą zastąpione wartością przypisaną do danej nazwy.

Przykład:

# define identyfikator wartości

Stały identyfikator spełnia te same zasady, regulujące nazwy zmiennych. Wartość może być dowolna:

Przykład:

#define ABC 100
#define PI 0.314
#define COMPANY_NAME "MetaQuotes Software Corp"
...
void PokazCopyright()
{
Print ( "Copyright © 2004-2009," COMPANY_NAME);
Print ( "http://www.twojadomena.pl");
}

Dyrektywa #property

Dyrektywa #property służy do określenia, parametrów i właściwości tworzonego programu i to właśnie dzięki tej dyrektywie między innymi możemy określić czy kod wykonywalny będzie rozpoznawalny, jako EA, wskaźnik, skrypt lub biblioteka. Od określonego typu kodu wykonywalnego zależy też możliwość stosowania kolejnych właściwości dyrektywy #property przykładowo parametry show_confirm, show_inputs stosujemy tylko w skryptach, a indicator buffer tylko dla wskaźników.

Przykład:

#property identyfikatora wartość

Poniżej zamieszczono dopuszczalne parametry dyrektywy #property

stała

link

wartość

opis

string

Link do strony www

stała

copyright

wartość

opis

string

Nazwa i prawa autorskie tworzącego oprogramowanie

stała

stacksize

wartość

opis

int

Wielkość stosu

stała

library

wartość

opis

 

Oznacza, że program będzie przechowywał tylko funkcje i nie posiada funkcji specjalnych takich jak start()

stała

indicator_chart_window

wartość

opis

void

Pokazuj wykres wskaźnika w oknie danych

stała

indicator_separate_window

wartość

opis

void

Pokazuj wykres wskaźnika w osobnym oknie

stała

indicator_buffers

wartość

opis

int

Określa maksymalną ilość buforów dla przechowywania danych wyliczonego wskaźnika, pokazywanych na wykresie, wartość od 1do 8

stała

indicator_minimum

wartość

opis

double

Określa minimalną wartość na skali wskaźnika

stała

indicator_maximum

wartość

opis

double

Określa maksymalną wartość na skali wskaźnika

stała

indicator_colorN

wartość

opis

color

Określa kolor linii wskaźnika, gdzie N przyjmuje wartości od 1 do 8

stała

indicator_widthN

wartość

opis

int

Określa szerokość linii wskaźnika, gdzie N przyjmuje wartości od 1 do 8

stała

indicator_styleN

wartość

opis

int

Określa styl linii, gdzie N przyjmuje wartości od 1 do 8

stała

indicator_levelN

wartość

opis

double

Określa poziom dla poszczególnych, oddzielnych okien wskaźniaka, gdzie N przyjmuje wartości od 1 do 8

stała

indicator_levelcolor

wartość

opis

color

Określa kolor linii poziomu

stała

indicator_levelwidth

wartość

opis

int

Określa szerokość linii poziomu

stała

indicator_levelstyle

wartość

opis

int

Określa styl linii poziomu

stała

show_confirm

wartość

opis

void

Wykorzystywane tylko przez wskaźnik

stała

show_inputs

wartość

opis

void

Przed uruchomieniem skryptu, zobaczymy jego właściwości

stała wartość opis
link string Link do strony www
copyright string Nazwa i prawa autorskie tworzącego oprogramowanie
stacksize int Wielkość stosu
library   Oznacza, że program będzie przechowywał tylko funkcje i nie posiada funkcji specjalnych takich jak start()
indicator_chart_window void Pokazuj wykres wskaźnika w oknie danych
indicator_separate_window void Pokazuj wykres wskaźnika w osobnym oknie
indicator_buffers int Określa maksymalną ilość buforów dla przechowywania danych wyliczonego wskaźnika, pokazywanych na wykresie, wartość od 1do 8
indicator_minimum double Określa minimalną wartość na skali wskaźnika
indicator_maximum double Określa maksymalną wartość na skali wskaźnika
indicator_colorN color Określa kolor linii wskaźnika, gdzie N przyjmuje wartości od 1 do 8
indicator_widthN int Określa szerokość linii wskaźnika, gdzie N przyjmuje wartości od 1 do 8
indicator_styleN int Określa styl linii, gdzie N przyjmuje wartości od 1 do 8
indicator_levelN double Określa poziom dla poszczególnych, oddzielnych okien wskaźniaka, gdzie N przyjmuje wartości od 1 do 8
indicator_levelcolor color Określa kolor linii poziomu
indicator_levelwidth int Określa szerokość linii poziomu
indicator_levelstyle int Określa styl linii poziomu
show_confirm void Wykorzystywane tylko przez wskaźnik
show_inputs void Przed uruchomieniem skryptu, zobaczymy jego właściwości

Przykład:

#property link "http://www.metaquotes.net"
#property copyright "MetaQuotes Software Corp"
#property liblary
#property stacksize 1024

Dyrektywa #include

Dyrektywa #include służy do włączenia do kodu źródłowego znajdującego się w innych pilkach. Najczęściej wykorzystuje się to razem z plikami nagłówkowymi mqh, w których przechowywane są deklaracje zmiennych i funkcji. Wraz z dyrektywą #include musimy podać wartość, którą jest plik mający być dołączony do kodu programu. Mamy dwie możliwości określenia ujęcia nazwy pliku w nawias ostry(<>) wówczas plik będzie poszukiwany w katalogu specjalnie przygotowanym dla plików nagłówkowych (terminal_directory\experts\include) lub jeśli nazwa pliku jest ujęta w cudzysłów ("") to plik będzie poszukiwany w bieżącej lokalizacji pliku kodu źródłowego, czyli zależnie od tego czy to jest EA (terminal_directory\experts), wskaźnik (terminal_directory\experts\indicators) czy skrypt (terminal_directory\experts\scripts).

Przykład:

#include <nazwa_pliku>
#include "nazwa_pliku"

Przykład:

#include <WinUser32.mqh>
#include "mojabiblioteka.mqh"

Dyrektywa #import

Dyrektywa import służy do importowania funkcji z bibliotek zewnętrznych EX4 lub DLL. Aby dokonać importu funkcji z bibliotek zewnętrznych musimy wywołać dyrektywę #import, po czym podać parametr typu string, który jest nazwą pliku biblioteki. Po wywołaniu dyrektywy #import możemy zadeklarować funkcję oraz  jej parametry przekazywane i wartości zwracane.

Przykład:

# import "nazwa_pliku"
func1;
func2;
...
funcN;
# import

Funkcje importowane muszą posiadać swoje unikatowe nazwy. Funkcje o tych samych nazwach nie mogą być wywoływane z różnych bibliotek, a także niedozwolone jest, aby nazwy funkcji importowych były takie same jak słowa zastrzeżone języka MQL4 (np. nazwy typów danych, nazw funkcji wbudowanych). Kompilator języka MQL4 nie ma możliwości weryfikacji parametrów wewnętrznych zdefiniowanych w plikach bibliotek, dlatego należy zwrócić szczególną uwagę, aby deklaracje funkcji były dokładnie takie same jak te wewnątrz biblioteki. Bardzo istotnym jest też, aby typ danych, był spójny. Dopuszczalne jest rzutowanie typów danych np. typu AnsiChar do typu string lub typu single do typu double, ale należ być przy tym bardzo uważnym i zadbać o prawidłowość parametrów przekazywanych oraz zwracanych przez funkcję.

Przykład:

#import "user32.dll"
int MessageBoxA(int hWnd, string lpText, string lpCaption, int uType);

# import "stdlib.ex4"
ErrorDescription(int error_code);
int RGB(int red_value, int green_value, int blue_value);
bool CompareDoubles(double liczba1, double liczba2);
string DoubleToStrMorePrecision(double liczba, int precyzja);
string IntegerToHexString(int integer_number);

# import "ExpertSample.dll"
int GetIntValue(int);
double GetDoubleValue(double value);
string GetStringValue(string value);
double GetArrayItemValue(double tab[], int, int);
bool SetArrayItemValue (double & arr[], int, int, double);
podwójne GetRatesItemValue (double tablica[][6], int, int, int);
int SortStringArray(string & tablica[], int);
int ProcessStringArray (string & tablica[], int);
# import

Proszę zwrócić uwagę, że nie ma konieczności podawania nazw parametrów wywoływanych przez funkcje, istotne jest natomiast określenie jakiego typu mają być to parametry. Nie jest zalecane podawanie pełnej ścieżki do pliku bibliotek. Zalecane jest natomiast umieszczania w specjalnie do tego przeznaczonym podkatalogu (terminal_directory\experts\liblaries). Jeśli podana nazwa pliku biblioteki nie zostanie odnaleziona w katalogu domyślnym to w drugiej kolejności zostanie sprawdzone czy nie ma jej w katalogu głównym EA (terminal_directory\experts).
 

Niniejszy materiał, przygotowany przez DM BOŚ S.A. ma charakter wyłącznie informacyjny... Dowiedz się więcej
1/1