4. MQL4 dla początkujących. Część IV.

4.2. Przykład kombinacji operatorów switch ... case, for oraz if

W poprzednim rozdziale szukaliśmy najwyższą cenę high oraz czas utworzenia odpowiedniej świecy. W oparciu o tamten kod stwórzmy teraz skrypt, który będzie umiał zrealizować jedno z dwóch zadań: albo (a) znaleźć najwyższą cenę high i czas utworzenia tej świecy albo (b) znaleźć najniższą cenę low i czas utworzenia tej świecy.

Jeśli po przeczytaniu poprzedniego rozdziału nadal masz trudności ze zrozumieniem tamtego kodu, proponuję jeszcze raz go przeanalizować, ponieważ nie chcę aby poniższy materiał dydaktyczny spowodował detonację twojego mózgu. Uważam, że nauka powinna odbywać się w sposób harmonijny, bez przeskoków do trudniejszego materiału bez zrozumienia poprzedniego, następstwem czego może być zniechęcenie do dalszej nauki. Jeśli jesteś gotów na kolejną dawkę adrenaliny to idziemy dalej.

Dla lepszego zobrazowania programu, podzielę go na 3 części. W 1-ej przygotujemy parametry wejściowe, w 2-ej części w ciele głównej funkcji skryptu OnStart() najpierw sprawdzimy poprawność wejściowego parametru i zainicjalizujemy 2 zmienne, a następnie w 3-ej części za pomocą operatora-przełącznika switch skierujemy skrypt do realizacji odpowiedniego działania. Po omówieniu każdej z tych części z osobna na końcu połączymy je w całość.


Część 1


Kod 1
#property strict
#property script_show_inputs
//--- CZĘŚĆ 1
input ushort BarsNumber = 1000;  // Ilość świec (do 65 535).
enum  find
  {
   a = 0,    // High
   b = 1,    // Low
  };
input find   Price = 0; // Wybierz żądaną cenę.
//---
void OnStart()
  {
   //--- CZĘŚĆ 2
   //--- CZĘŚĆ 3
  }

Tutaj tworzymy zewnętrzną zmienną BarsNumber typu ushort, której można będzie przypisać liczbę całkowitą z zakresu od 0 do 65535 , o czym poinformujemy użytkownika komentarzem // Ilość świec (do 65 535). Następnie za pomocą słowa kluczowego enum tworzymy wyliczenie o nazwie find. W ciele tego wyliczenia utworzymy listę wartości składającą się z 2 członów, którym przypisujemy wartości 0 i 1. Dalej tworzymy zmienną Price będącą wyliczeniem od find, której domyślnie przypiszemy 0, a za pomocą input uczynimy z niej parametr zewnętrzny. Po uruchomieniu tego skryptu (kod 1) w MetaTrader 4 pojawi się następujące okienko (rys 1).

Rys. 1. Okienko z zewnętrznymi parametrami.


Część 2


Kod 2
#property strict
#property script_show_inputs
//--- CZĘŚĆ 1

void OnStart()
  {
//--- CZĘŚĆ 2

//--- sprawdzamy, czy ilość przeszukiwanych świec nie przekracza ilość dostępną na wykresie
   if(BarsNumber > Bars)
     {
      Print("Na wykresie jest mniej niż " , IntegerToString(BarsNumber) , " świec.");
      return;
     }
//--- tworzymy zmienne do przechowywania ceny (high lub low) oraz daty i czasu utworzenia świecy
   double   Value     = 0.0;
   datetime TimeValue = 0;

//--- CZĘŚĆ 3
  }

Tutaj w pierwszej kolejności za pomocą operatora if sprawdzamy czy na wykresie jest wystarczająca ilość świec. Gdyby użytkownik chciał przeszukać np. 50000 świec, a w ustawieniach MetaTrader 4 ograniczyłby ilość świec na wykresie do 10000, wtedy w trakcie działania skryptu pojawi się błąd 'array out of range in 'Test.mq4', tj. skrypt wyjdzie poza zakres tablicy przeszukiwanych świec. Zadaną ilość świec będziemy porównywać z predefiniowaną zmienną Bars , która jest samodzielnie tworzona w momencie uruchomienia skryptu na wykresie notowań. Gdyby się okazało, że warunek BarsNumber > Bars to prawda, wtedy zostaną uruchomione dwie instrukcje zapisane między klamrami. Najpierw funkcja Print() w logach terminala wyświetli stosowną informację, a następnie operator przerywania return od razu przerwie cały skrypt.

Jeśli po tym sprawdzeniu wszystko okaże się dobrze skrypt utworzy zmienne Value oraz TimeValue, przypisze im zerowe wartości i przejdzie do trzeciej części, gdzie operatorem-przełącznikiem switch skierujemy program na odpowiednie tory.


Część 3


Kod 3
#property strict
#property script_show_inputs
//--- CZĘŚĆ 1

void OnStart()
  {
//--- CZĘŚĆ 2
//--- CZĘŚĆ 3
   switch(Price)
     {

      //--- CZĘŚĆ 3.1
      case 0:
         for(...)
            if(...)
              {
               //...
              }
         Print(...);
         break;

      //--- CZĘŚĆ 3.2
      case 1:
         for(...)
            if(...)
              {
               //...
              }
         Print();
         break;
     }
  }

W kodzie 3 przedstawiłem uproszczony schemat trzeciej części. Jeśli w okienku wyboru zewnętrznych parametrów (rys. 1) wybierzemy "High", wtedy w pierwszej części kodu program ustawi Price = 0. Ta wartość zostanie podstawiona do nagłówka switch, który skieruje program na etykietę case 0 (część 3.1). Jeśli wybierzemy "Low", wtedy Price = 1 i switch skieruje program na case 1 (część 3.2).


Część 3.1


Kod 4
      //--- CZĘŚĆ 3.1
      //--- szukamy najwyższy high
      case 0:
         Value = High[0]; // zmiennej Value przypisujemy wartość high świecy z indeksem 0
         //---
         for(int i = 1; i < BarsNumber; i++)
            if(High[i] > Value)
              {
               Value = High[i]; TimeValue = Time[i];
              }
         //--- wyświetlić wynik o świecy z najwyższym high
         Print("Wśród " , IntegerToString(BarsNumber) ,
               " świec, świeca z datą utworzenia " , TimeToString(TimeValue) ,
               " ma najwyższy high " , DoubleToString(Value , _Digits));
         break;

W zasadzie działa to identycznie do kodu opisanego w poprzednim rozdziale z tą zaś różnicą, że tu przed pętlą for zmiennej Value przypisujemy wartość high świecy z indeksem 0 (Value = High[0]). Licznik w for zaczynamy od 1 (i = 1). Dalej if zacznie szukać najwyższą cenę high oraz czas i datę utworzenia odpowiedniej świecy. Po zakończeniu pętli funkcja Print() wyświetli te dane w dzienniku logów terminala MT4. Potem program napotka się na break, który nakaży programowi z tego miejsca wyjść z switch. W tym przypadku działania zapisane w kolejnej etykiecie case 1 nie zostaną zrealizowane. Po switch nic już nie ma, w tym momencie skrypt zakończy swoje działanie i wyładuje się z MetaTrader 4.


Część 3.2


Kod 5
      //--- CZĘŚĆ 3.2
      //--- szukamy najniższy low
      case 1:
         Value = Low[0]; // zmiennej Value przypisujemy wartość low świecy z indeksem 0
         //---
         for(int i = 1; i < BarsNumber; i++)
            if(Low[i] < Value)
              {
               Value = Low[i]; TimeValue = Time[i];
              }
         //--- wyświetlić wynik o świecy z najniższym low
         Print("Wśród " , IntegerToString(BarsNumber) ,
               " świec, świeca z datą utworzenia " , TimeToString(TimeValue) ,
               " ma najniższy low " , DoubleToString(Value , _Digits));
         break;

W przypadku jeśli będziemy szukać najniższą cenę low, wtedy Price będzie równe 1, a switch skieruje program na etykietę case 1. Tutaj Value przypisujemy wartość low świecy z indeksem 0 (Value = Low[0]), for rozpocznie poszukiwania od świecy z indeksem 1, if znajdzie najniższe low oraz datę i czas odpowiedniej świecy, a Print() wyświetli te dane. Na końcu tej etykiety operator przerywania break przerwie switch po czym skrypt zakończy swoje działanie.


A tak będzie wyglądać kod źródłowy zebrany w jedną całość.

Kod 6
#property strict
#property script_show_inputs

//--- CZĘŚĆ 1
input ushort BarsNumber = 1000;  // Ilość świec (do 65 535).
enum  find
  {
   a = 0,    // High
   b = 1,    // Low
  };
input find   Price = 0; // Wybierz żądaną cenę.
//---
void OnStart()
  {
//--- CZĘŚĆ 2
//--- sprawdzamy, czy ilość przeszukiwanych świec nie przekracza ilość dostępną na wykresie
   if(BarsNumber > Bars)
     {
      Print("Na wykresie jest mniej niż " , IntegerToString(BarsNumber) , " świec.");
      return;
     }
//--- tworzymy zmienne do przechowywania ceny (high lub low) oraz daty i czasu utworzenia świecy
   double   Value     = 0.0;
   datetime TimeValue = 0;

//--- CZĘŚĆ 3
   switch(Price)
     {
      //--- CZĘŚĆ 3.1
      //--- szukamy najwyższy high
      case 0:
         Value = High[0]; // zmiennej Value przypisujemy wartość high świecy z indeksem 0
         //---
         for(int i = 1; i < BarsNumber; i++)
            if(High[i] > Value)
              {
               Value = High[i]; TimeValue = Time[i];
              }
         //--- wyświetlić wynik o świecy z najwyższym high
         Print("Wśród " , IntegerToString(BarsNumber) ,
               " świec, świeca z datą utworzenia " , TimeToString(TimeValue) ,
               " ma najwyższy high " , DoubleToString(Value , _Digits));
         break;
      //--- CZĘŚĆ 3.2
      //--- szukamy najniższy low
      case 1:
         Value = Low[0]; // zmiennej Value przypisujemy wartość low świecy z indeksem 0
         //---
         for(int i = 1; i < BarsNumber; i++)
            if(Low[i] < Value)
              {
               Value = Low[i]; TimeValue = Time[i];
              }
         //--- wyświetlić wynik o świecy z najniższym low
         Print("Wśród " , IntegerToString(BarsNumber) ,
               " świec, świeca z datą utworzenia " , TimeToString(TimeValue) ,
               " ma najniższy low " , DoubleToString(Value , _Digits));
         break;
     }
  }

Znawca MQL4 mógłby mi tutaj zarzucić, że zamiast kombinacji for z if można było by po prostu zastosować funkcję iHighest() do znalezienia indeksu świecy z najwyższą ceną high lub iLowest() dla najniższej ceny low. Tak, są takie funkcje, jednak na tym przykładzie chciałem pokazać sposoby łączenia ze sobą tych operatorów.