English 中文 Español Deutsch 日本語 Português
preview
Рецепты MQL5 — База данных макроэкономических событий

Рецепты MQL5 — База данных макроэкономических событий

MetaTrader 5Примеры | 31 января 2023, 13:02
1 232 0
Denis Kirichenko
Denis Kirichenko

Введение

В данной статье речь пойдёт о том, как группировать и управлять данными, которые описывают макроэкономические события календаря.

Волей-неволей в современном мире, где информационные потоки носят всепроникающий характер, при анализе событий приходится иметь дело с big data. И хотя, в большей степени в статье освещаются вопросы, связанные не с содержанием, а формой, тем не менее представляется, что правильная организация и структурирование данных немало способствует тому, что эти данные превратятся в информацию.

Поставленные задачи будем решать средствами SQLite. Отмечу, что разработчик добавил поддержку работы с базами данных SQLite прямо из MQL5 в билде 2265 (6 декабря 2019). До этого приходилось использовать различные коннекторы, например, как это описано в статье SQL и MQL5: Работаем с базой данных SQLite.


1. Документация и дополнительный материал

Весьма бегло пройдёмся по Документации в части работы с базами данных. Разработчик предоставляет 26 нативных функций:

  1. DatabaseOpen();
  2. DatabaseClose();
  3. DatabaseImport();
  4. DatabaseExport();
  5. DatabasePrint();
  6. DatabaseTableExists();
  7. DatabaseExecute();
  8. DatabasePrepare();
  9. DatabaseReset();
  10. DatabaseBind();
  11. DatabaseBindArray();
  12. DatabaseRead();
  13. DatabaseReadBind();
  14. DatabaseFinalize();
  15. DatabaseTransactionBegin();
  16. DatabaseTransactionCommit();
  17. DatabaseTransactionRollback();
  18. DatabaseColumnsCount();
  19. DatabaseColumnName();
  20. DatabaseColumnType();
  21. DatabaseColumnSize();
  22. DatabaseColumnText();
  23. DatabaseColumnInteger();
  24. DatabaseColumnLong();
  25. DatabaseColumnDouble();
  26. DatabaseColumnBlob().

Ещё есть блок статистических и блок математических функций, которые были добавлены совсем недавно. Ну и конечно отправной точкой для изучения этого функционала может послужить статья SQLite: нативная работа с базами данных на SQL в MQL5.


2. Класс базы данных CDatabase

Для удобства работы с базами данных создадим класс CDatabase. Сначала опишем состав класса. Затем на примерах проверим его работу.

В состав данных-членов класса CDatabase входят следующие:

  • m_name  - это имя файла базы данных (с расширением);
  • m_handle - хэндл базы данных;
  • m_flags - комбинация флагов;
  • m_table_names – имена таблиц;
  • m_curr_table_name – имя текущей таблицы;
  • m_sql_request_ha – хэндл последнего SQL-запроса;
  • m_sql_request – последний SQL-запрос.

Что касается методов, то их разбил бы на несколько групп:

  1. Методы, включающие в себя нативные функции для работы с базами данных (API MQL5 functions);
  2. Методы для работы с таблицами;
  3. Методы для работы с запросами;
  4. Методы для работы с представлениями;
  5. Методы получения значений членов-данных (get-методы).

Хотелось бы отметить следующий момент. В SQLite есть множество форм запросов – от простых до сложных. Не ставилось целью создать свой метод в классе CDatabase под каждую такую форму.  Если в классе и отсутствует метод под тот или иной запрос, то запрос можно сформировать с помощью универсального метода CDatabase::Select().

 

Теперь рассмотрим на примерах, как можно использовать возможности класса CDatabase.


2.1 Создание базы данных

Создадим нашу первую календарную базу с помощью скрипта 1_create_calendar_db.mq5.  В скрипте будет всего несколько строчек кода.

//--- include
#include "..\CDatabase.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   CDatabase db_obj;
   string file_name="Databases\\test_calendar_db.sqlite";
   uint flags=DATABASE_OPEN_READWRITE | DATABASE_OPEN_CREATE;
   if(!db_obj.Open(file_name, flags))
      ::PrintFormat("Failed to create a calendar database \"%s\"!", file_name);
   db_obj.Close();
  }
//+------------------------------------------------------------------+

После запуска скрипта, увидим, что в папке %MQL5\Files\Databases появился файл базы данных test_calendar_db.sqlite (Рис.1).


Файл базы данных test_calendar_db.sqlite

Рис.1 Файл базы данных test_calendar_db.sqlite


Если дальше откроем этот файл в редакторе кода, то увидим в свою очередь, что база данных пустая (Рис.2).


База данных test_calendar_db

Рис.2 База данных test_calendar_db


2.2 Создание таблицы

Попробуем заполнить базу данных. С этой целью создадим таблицу COUNTRIES, в которую занесём список стран, календарные события которых впоследствии будут обрабатываться нашими запросами. С этой задачей справится скрипт 2_create_countries_table.mq5.

//--- include
#include "..\CDatabase.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
//--- open a database
   CDatabase db_obj;
   string file_name="Databases\\test_calendar_db.sqlite";
   uint flags=DATABASE_OPEN_READWRITE;
   if(!db_obj.Open(file_name, flags))
      {
      ::PrintFormat("Failed to open a calendar database \"%s\"!", file_name);
      db_obj.Close();
      return;
      }
//--- create a table
   string table_name="COUNTRIES";
   string params[]=
      {
      "COUNTRY_ID UNSIGNED BIG INT PRIMARY KEY NOT NULL,", // 1) country ID
      "NAME TEXT,"                                         // 2) country name
      "CODE TEXT,"                                         // 3) country code
      "CONTINENT TEXT,"                                    // 4) country continent
      "CURRENCY TEXT,"                                     // 5) currency code
      "CURRENCY_SYMBOL TEXT,"                              // 6) currency symbol
      "URL_NAME TEXT"                                      // 7) country URL
      };
   if(!db_obj.CreateTable(table_name, params))
      {
      ::PrintFormat("Failed to create a table \"%s\"!", table_name);
      db_obj.Close();
      return;
      }
   db_obj.Close();
   }
//+------------------------------------------------------------------+

После запуска скрипта можно обнаружить, что в БД появилась таблица COUNTRIES (Рис.3).


Пустая таблица COUNTRIES

Рис.3  Пустая таблица COUNTRIES


2.3 Заполнение таблицы

Заполним новую таблицу данными. Для этого воспользуемся возможностями класса CiCalendarInfo. Более подробно о классе написано в статье «Рецепты MQL5 – Экономический календарь». Непосредственно задачу выполнит скрипт 3_fill_in_countries_table.mq5.

//--- include
#include "..\CalendarInfo.mqh"
#include "..\CDatabase.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
//--- open a database
   CDatabase db_obj;
   string file_name="Databases\\test_calendar_db.sqlite";
   uint flags=DATABASE_OPEN_READWRITE;
   if(!db_obj.Open(file_name, flags))
      {
      db_obj.Close();
      return;
      }
//--- open a table
   string table_name="COUNTRIES";
   if(db_obj.SelectTable(table_name))
      if(db_obj.EmptyTable())
         {
         db_obj.FinalizeSqlRequest();
         string col_names[]=
            {
            "COUNTRY_ID", "NAME", "CODE", "CONTINENT",
            "CURRENCY", "CURRENCY_SYMBOL", "URL_NAME"
            };
//--- fill in the table
         CiCalendarInfo calendar_info;
         if(calendar_info.Init())
            {
            MqlCalendarCountry calendar_countries[];
            if(calendar_info.GetCountries(calendar_countries))
               {
               if(db_obj.TransactionBegin())
                  for(int c_idx=0; c_idx<::ArraySize(calendar_countries); c_idx++)
                     {
                     MqlCalendarCountry curr_country=calendar_countries[c_idx];
                     string col_vals[];
                     ::ArrayResize(col_vals, 7);
                     col_vals[0]=::StringFormat("%I64u", curr_country.id);
                     col_vals[1]=::StringFormat("'%s'", curr_country.name);
                     col_vals[2]=::StringFormat("'%s'", curr_country.code);
                     col_vals[3]="NULL";
                     SCountryByContinent curr_country_continent_data;
                     if(curr_country_continent_data.Init(curr_country.code))
                        col_vals[3]=::StringFormat("'%s'",
                                                   curr_country_continent_data.ContinentDescription());
                     col_vals[4]=::StringFormat("'%s'", curr_country.currency);
                     col_vals[5]=::StringFormat("'%s'", curr_country.currency_symbol);
                     col_vals[6]=::StringFormat("'%s'", curr_country.url_name);
                     if(!db_obj.InsertSingleRow(col_names, col_vals))
                        {
                        db_obj.TransactionRollback();
                        db_obj.Close();
                        return;
                        }
                     db_obj.FinalizeSqlRequest();
                     }
               if(!db_obj.TransactionCommit())
                  ::PrintFormat("Failed to complete transaction execution, error %d", ::GetLastError());
               }
            //--- print
            if(db_obj.PrintTable()<0)
               ::PrintFormat("Failed to print the table \"%s\", error %d", table_name, ::GetLastError());
            }
         }
   db_obj.Close();
   }
//+------------------------------------------------------------------+

В журнале распечатаем данные таблицы COUNTRIES.

3_fill_in_countries_table (EURUSD,H1)    #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY CURRENCY_SYMBOL URL_NAME      
3_fill_in_countries_table (EURUSD,H1)   --+-----------------------------------------------------------------------------------------
3_fill_in_countries_table (EURUSD,H1)    1|        554 New Zealand    NZ   Australia/Oceania NZD      $               new-zealand    
3_fill_in_countries_table (EURUSD,H1)    2|        999 European Union EU   Europe            EUR      €               european-union 
3_fill_in_countries_table (EURUSD,H1)    3|        392 Japan          JP   Asia              JPY      ¥               japan          
3_fill_in_countries_table (EURUSD,H1)    4|        124 Canada         CA   North America     CAD      $               canada         
3_fill_in_countries_table (EURUSD,H1)    5|         36 Australia      AU   Australia/Oceania AUD      $               australia      
3_fill_in_countries_table (EURUSD,H1)    6|        156 China          CN   Asia              CNY      ¥               china          
3_fill_in_countries_table (EURUSD,H1)    7|        380 Italy          IT   Europe            EUR      €               italy          
3_fill_in_countries_table (EURUSD,H1)    8|        702 Singapore      SG   Asia              SGD      R$              singapore      
3_fill_in_countries_table (EURUSD,H1)    9|        276 Germany        DE   Europe            EUR      €               germany        
3_fill_in_countries_table (EURUSD,H1)   10|        250 France         FR   Europe            EUR      €               france         
3_fill_in_countries_table (EURUSD,H1)   11|         76 Brazil         BR   South America     BRL      R$              brazil         
3_fill_in_countries_table (EURUSD,H1)   12|        484 Mexico         MX   North America     MXN      Mex$            mexico         
3_fill_in_countries_table (EURUSD,H1)   13|        710 South Africa   ZA   Africa            ZAR      R               south-africa   
3_fill_in_countries_table (EURUSD,H1)   14|        344 Hong Kong      HK   Asia              HKD      HK$             hong-kong      
3_fill_in_countries_table (EURUSD,H1)   15|        356 India          IN   Asia              INR      ₹               india          
3_fill_in_countries_table (EURUSD,H1)   16|        578 Norway         NO   Europe            NOK      Kr              norway         
3_fill_in_countries_table (EURUSD,H1)   17|        840 United States  US   North America     USD      $               united-states  
3_fill_in_countries_table (EURUSD,H1)   18|        826 United Kingdom GB   Europe            GBP      £               united-kingdom 
3_fill_in_countries_table (EURUSD,H1)   19|        756 Switzerland    CH   Europe            CHF      ₣               switzerland    
3_fill_in_countries_table (EURUSD,H1)   20|        410 South Korea    KR   Asia              KRW      ₩               south-korea    
3_fill_in_countries_table (EURUSD,H1)   21|        724 Spain          ES   Europe            EUR      €               spain          
3_fill_in_countries_table (EURUSD,H1)   22|        752 Sweden         SE   Europe            SEK      Kr              sweden         
3_fill_in_countries_table (EURUSD,H1)   23|          0 Worldwide      WW   World             ALL                      worldwide      


В MetaEditor таблица имеет следующий вид (Рис.4).

Заполненная таблица COUNTRIES

Рис.4 Заполненная таблица COUNTRIES



2.4 Выбор некоторых столбцов таблицы

Поработаем с данными таблицы COUNTRIES. Допустим, что нужно выбрать такие столбцы:

  • "COUNTRY_ID";
  • "COUNTRY_NAME";
  • "COUNTRY_CODE";
  • "COUNTRY_CONTINENT";
  • "CURRENCY".

Создадим SQL-запрос с помощью скрипта 4_select_some_columns.mq5 следующим образом:

//--- include
#include "..\CDatabase.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
//--- open a database
   CDatabase db_obj;
   string file_name="Databases\\test_calendar_db.sqlite";
   uint flags=DATABASE_OPEN_READONLY;
   if(!db_obj.Open(file_name, flags))
      {
      db_obj.Close();
      return;
      }
//--- check a table
   string table_name="COUNTRIES";
   if(db_obj.SelectTable(table_name))
      {
      string col_names_to_select[]=
         {
         "COUNTRY_ID", "NAME", "CODE",
         "CONTINENT", "CURRENCY"
         };
      if(!db_obj.SelectFrom(col_names_to_select))
         {
         db_obj.Close();
         return;
         }
      //--- print the SQL request
      if(db_obj.PrintSqlRequest()<0)
         ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
      db_obj.FinalizeSqlRequest();
      }
   db_obj.Close();
   }
//+------------------------------------------------------------------+


При распечатывании запроса получим:

4_select_some_columns (EURUSD,H1)        #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY
4_select_some_columns (EURUSD,H1)       --+----------------------------------------------------------
4_select_some_columns (EURUSD,H1)        1|        554 New Zealand    NZ   Australia/Oceania NZD      
4_select_some_columns (EURUSD,H1)        2|        999 European Union EU   Europe            EUR      
4_select_some_columns (EURUSD,H1)        3|        392 Japan          JP   Asia              JPY      
4_select_some_columns (EURUSD,H1)        4|        124 Canada         CA   North America     CAD      
4_select_some_columns (EURUSD,H1)        5|         36 Australia      AU   Australia/Oceania AUD      
4_select_some_columns (EURUSD,H1)        6|        156 China          CN   Asia              CNY      
4_select_some_columns (EURUSD,H1)        7|        380 Italy          IT   Europe            EUR      
4_select_some_columns (EURUSD,H1)        8|        702 Singapore      SG   Asia              SGD      
4_select_some_columns (EURUSD,H1)        9|        276 Germany        DE   Europe            EUR      
4_select_some_columns (EURUSD,H1)       10|        250 France         FR   Europe            EUR      
4_select_some_columns (EURUSD,H1)       11|         76 Brazil         BR   South America     BRL      
4_select_some_columns (EURUSD,H1)       12|        484 Mexico         MX   North America     MXN      
4_select_some_columns (EURUSD,H1)       13|        710 South Africa   ZA   Africa            ZAR      
4_select_some_columns (EURUSD,H1)       14|        344 Hong Kong      HK   Asia              HKD      
4_select_some_columns (EURUSD,H1)       15|        356 India          IN   Asia              INR      
4_select_some_columns (EURUSD,H1)       16|        578 Norway         NO   Europe            NOK      
4_select_some_columns (EURUSD,H1)       17|        840 United States  US   North America     USD      
4_select_some_columns (EURUSD,H1)       18|        826 United Kingdom GB   Europe            GBP      
4_select_some_columns (EURUSD,H1)       19|        756 Switzerland    CH   Europe            CHF      
4_select_some_columns (EURUSD,H1)       20|        410 South Korea    KR   Asia              KRW      
4_select_some_columns (EURUSD,H1)       21|        724 Spain          ES   Europe            EUR      
4_select_some_columns (EURUSD,H1)       22|        752 Sweden         SE   Europe            SEK      
4_select_some_columns (EURUSD,H1)       23|          0 Worldwide      WW   World             ALL      

Очевидно, что выборка была сделана без какой-либо сортировки.


2.5 Выбор некоторых отсортированных столбцов таблицы

Попробуем упорядочить данные в таблице по столбцу "COUNTRY_ID". Этот запрос имеет такую реализацию в скрипте 5_select_some_sorted_columns.mq5:

//--- include
#include "..\CDatabase.mqh"
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
   {
//--- open a database
   CDatabase db_obj;
   string file_name="Databases\\test_calendar_db.sqlite";
   uint flags=DATABASE_OPEN_READONLY;
   if(!db_obj.Open(file_name, flags))
      {
      db_obj.Close();
      return;
      }
//--- check a table
   string table_name="COUNTRIES";
   if(db_obj.SelectTable(table_name))
      {
      string col_names_to_select[]=
         {
         "COUNTRY_ID", "NAME", "CODE",
         "CONTINENT", "CURRENCY"
         };
      string ord_names[1];
      ord_names[0]=col_names_to_select[0];
      if(!db_obj.SelectFromOrderedBy(col_names_to_select, ord_names))
         {
         db_obj.Close();
         return;
         }
      //--- print the SQL request
      if(db_obj.PrintSqlRequest()<0)
         ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
      db_obj.FinalizeSqlRequest();
      }
   db_obj.Close();
   }
//+------------------------------------------------------------------+


В журнале появится результат выполнения запроса:

5_select_some_sorted_columns (EURUSD,H1)         #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY
5_select_some_sorted_columns (EURUSD,H1)        --+----------------------------------------------------------
5_select_some_sorted_columns (EURUSD,H1)         1|          0 Worldwide      WW   World             ALL      
5_select_some_sorted_columns (EURUSD,H1)         2|         36 Australia      AU   Australia/Oceania AUD      
5_select_some_sorted_columns (EURUSD,H1)         3|         76 Brazil         BR   South America     BRL      
5_select_some_sorted_columns (EURUSD,H1)         4|        124 Canada         CA   North America     CAD      
5_select_some_sorted_columns (EURUSD,H1)         5|        156 China          CN   Asia              CNY      
5_select_some_sorted_columns (EURUSD,H1)         6|        250 France         FR   Europe            EUR      
5_select_some_sorted_columns (EURUSD,H1)         7|        276 Germany        DE   Europe            EUR      
5_select_some_sorted_columns (EURUSD,H1)         8|        344 Hong Kong      HK   Asia              HKD      
5_select_some_sorted_columns (EURUSD,H1)         9|        356 India          IN   Asia              INR      
5_select_some_sorted_columns (EURUSD,H1)        10|        380 Italy          IT   Europe            EUR      
5_select_some_sorted_columns (EURUSD,H1)        11|        392 Japan          JP   Asia              JPY      
5_select_some_sorted_columns (EURUSD,H1)        12|        410 South Korea    KR   Asia              KRW      
5_select_some_sorted_columns (EURUSD,H1)        13|        484 Mexico         MX   North America     MXN      
5_select_some_sorted_columns (EURUSD,H1)        14|        554 New Zealand    NZ   Australia/Oceania NZD      
5_select_some_sorted_columns (EURUSD,H1)        15|        578 Norway         NO   Europe            NOK      
5_select_some_sorted_columns (EURUSD,H1)        16|        702 Singapore      SG   Asia              SGD      
5_select_some_sorted_columns (EURUSD,H1)        17|        710 South Africa   ZA   Africa            ZAR      
5_select_some_sorted_columns (EURUSD,H1)        18|        724 Spain          ES   Europe            EUR      
5_select_some_sorted_columns (EURUSD,H1)        19|        752 Sweden         SE   Europe            SEK      
5_select_some_sorted_columns (EURUSD,H1)        20|        756 Switzerland    CH   Europe            CHF      
5_select_some_sorted_columns (EURUSD,H1)        21|        826 United Kingdom GB   Europe            GBP      
5_select_some_sorted_columns (EURUSD,H1)        22|        840 United States  US   North America     USD      
5_select_some_sorted_columns (EURUSD,H1)        23|        999 European Union EU   Europe            EUR      

Скрипт сработал правильно - столбец "COUNTRY_ID" начинается с 0, а заканчивается 999.


2.6 Выбор сгруппированных результатов заданного столбца таблицы

Теперь с помощью скрипта 6_select_some_grouped_columns.mq5 постараемся получить сгруппированные названия стран по континентам. Задача заключается в том, чтобы для каждой строки континента получить количество стран, в него входящих. Страны выбираются из столбца “NAME". После запуска скрипта в журнале появятся такие строчки:

6_select_some_grouped_columns (EURUSD,H1)       #| CONTINENT         COUNT(NAME)
6_select_some_grouped_columns (EURUSD,H1)       -+------------------------------
6_select_some_grouped_columns (EURUSD,H1)       1| Africa                      1 
6_select_some_grouped_columns (EURUSD,H1)       2| Asia                        6 
6_select_some_grouped_columns (EURUSD,H1)       3| Australia/Oceania           2 
6_select_some_grouped_columns (EURUSD,H1)       4| Europe                      9 
6_select_some_grouped_columns (EURUSD,H1)       5| North America               3 
6_select_some_grouped_columns (EURUSD,H1)       6| South America               1 
6_select_some_grouped_columns (EURUSD,H1)       7| World                       1 


Континент “Europe” включает в себя больше всего стран – 9, а континенты “Africa” и “South America”  - всего по 1. И к ним ещё присоединился весь мир – “World”.


2.7 Выбор уникальных значений заданного столбца таблицы

Теперь с помощью скрипта 7_select_distinct_columns.mq5 соберём уникальные значения в столбце "CURRENCY". Есть страны, где используется одна и та же валюта. Чтобы отсеять повторы, запустим этот скрипт и в журнале получим:

7_select_distinct_columns (EURUSD,H1)    1| NZD      
7_select_distinct_columns (EURUSD,H1)    2| EUR      
7_select_distinct_columns (EURUSD,H1)    3| JPY      
7_select_distinct_columns (EURUSD,H1)    4| CAD      
7_select_distinct_columns (EURUSD,H1)    5| AUD      
7_select_distinct_columns (EURUSD,H1)    6| CNY      
7_select_distinct_columns (EURUSD,H1)    7| SGD      
7_select_distinct_columns (EURUSD,H1)    8| BRL      
7_select_distinct_columns (EURUSD,H1)    9| MXN      
7_select_distinct_columns (EURUSD,H1)   10| ZAR      
7_select_distinct_columns (EURUSD,H1)   11| HKD      
7_select_distinct_columns (EURUSD,H1)   12| INR      
7_select_distinct_columns (EURUSD,H1)   13| NOK      
7_select_distinct_columns (EURUSD,H1)   14| USD      
7_select_distinct_columns (EURUSD,H1)   15| GBP      
7_select_distinct_columns (EURUSD,H1)   16| CHF      
7_select_distinct_columns (EURUSD,H1)   17| KRW      
7_select_distinct_columns (EURUSD,H1)   18| SEK      
7_select_distinct_columns (EURUSD,H1)   19| ALL      

Таким образом в календаре есть события всего по 18 валютам и одна группа событий, которая относится ко всем валютам.

Несложно заметить, что методы выбора сгруппированных результатов и выбора уникальных значений имеют сходство. Продемонстрируем это на примере.

Скрипт 8_compare_ grouped_and_distinct_columns.mq5 распечатает в журнале такие результаты:

8_compare_ grouped_and_distinct_columns (EURUSD,H1)     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     Method CDatabase::SelectFromGroupBy()
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     #| CONTINENT        
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     -+------------------
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     1| Africa            
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     2| Asia              
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     3| Australia/Oceania 
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     4| Europe            
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     5| North America     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     6| South America     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     7| World             
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     Method CDatabase::SelectDistinctFrom()
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     #| CONTINENT        
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     -+------------------
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     1| Australia/Oceania 
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     2| Europe            
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     3| Asia              
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     4| North America     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     5| South America     
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     6| Africa            
8_compare_ grouped_and_distinct_columns (EURUSD,H1)     7| World             


Методы вернули одинаковые по содержанию результаты потому, что для первого метода мы определили столбец "CONTINENT" как столбец (поле) группировки. Любопытно, что первый метод ещё и отсортировал нам выборку.


2.8 Выбор упорядоченных уникальных значений заданного столбца таблицы

Значения в столбце “CURRENCY” были отображены скриптом 7_select_distinct_columns.mq5 не в отсортированном виде. Давайте  сделаем выборку уже с сортировкой (скрипт 9_select_sorted_distinct_columns.mq5). И пускай критерием сортировки станет столбец "COUNTRY_ID". В итоге манипуляций в журнале получим:

9_select_sorted_distinct_columns (EURUSD,H1)     #| CURRENCY
9_select_sorted_distinct_columns (EURUSD,H1)    --+---------
9_select_sorted_distinct_columns (EURUSD,H1)     1| ALL      
9_select_sorted_distinct_columns (EURUSD,H1)     2| AUD      
9_select_sorted_distinct_columns (EURUSD,H1)     3| BRL      
9_select_sorted_distinct_columns (EURUSD,H1)     4| CAD      
9_select_sorted_distinct_columns (EURUSD,H1)     5| CNY      
9_select_sorted_distinct_columns (EURUSD,H1)     6| EUR      
9_select_sorted_distinct_columns (EURUSD,H1)     7| HKD      
9_select_sorted_distinct_columns (EURUSD,H1)     8| INR      
9_select_sorted_distinct_columns (EURUSD,H1)     9| JPY      
9_select_sorted_distinct_columns (EURUSD,H1)    10| KRW      
9_select_sorted_distinct_columns (EURUSD,H1)    11| MXN      
9_select_sorted_distinct_columns (EURUSD,H1)    12| NZD      
9_select_sorted_distinct_columns (EURUSD,H1)    13| NOK      
9_select_sorted_distinct_columns (EURUSD,H1)    14| SGD      
9_select_sorted_distinct_columns (EURUSD,H1)    15| ZAR      
9_select_sorted_distinct_columns (EURUSD,H1)    16| SEK      
9_select_sorted_distinct_columns (EURUSD,H1)    17| CHF      
9_select_sorted_distinct_columns (EURUSD,H1)    18| GBP      
9_select_sorted_distinct_columns (EURUSD,H1)    19| USD      


Теперь все валюты отсортированы. Причём по умолчанию сортировка проходит по возрастанию.


2.9 Выбор некоторых столбцов таблицы по условию

Ранее мы уже создавали SQL-запрос для выборки столбцов таблицы. Теперь сделаем так, чтобы можно было получить столбцы при выполнения некоторого условия. Допустим, что нужно отобрать страны, ID которых равен или больше 392 и равен или меньше 840. Эту задачу решает скрипт 10_select_some_columns_where.mq5.

После запуска скрипта в журнале увидим:

10_select_some_columns_where (EURUSD,H1)         #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY
10_select_some_columns_where (EURUSD,H1)        --+----------------------------------------------------------
10_select_some_columns_where (EURUSD,H1)         1|        392 Japan          JP   Asia              JPY      
10_select_some_columns_where (EURUSD,H1)         2|        410 South Korea    KR   Asia              KRW      
10_select_some_columns_where (EURUSD,H1)         3|        484 Mexico         MX   North America     MXN      
10_select_some_columns_where (EURUSD,H1)         4|        554 New Zealand    NZ   Australia/Oceania NZD      
10_select_some_columns_where (EURUSD,H1)         5|        578 Norway         NO   Europe            NOK      
10_select_some_columns_where (EURUSD,H1)         6|        702 Singapore      SG   Asia              SGD      
10_select_some_columns_where (EURUSD,H1)         7|        710 South Africa   ZA   Africa            ZAR      
10_select_some_columns_where (EURUSD,H1)         8|        724 Spain          ES   Europe            EUR      
10_select_some_columns_where (EURUSD,H1)         9|        752 Sweden         SE   Europe            SEK      
10_select_some_columns_where (EURUSD,H1)        10|        756 Switzerland    CH   Europe            CHF      
10_select_some_columns_where (EURUSD,H1)        11|        826 United Kingdom GB   Europe            GBP      
10_select_some_columns_where (EURUSD,H1)        12|        840 United States  US   North America     USD      


Т.е. выборка начинается с кода страны, который равен 392, а заканчивается кодом 840. 


2.10 Выбор некоторых отсортированных столбцов таблицы по условию

Усложним предыдущую задачу. Добавим к выборке критерий сортировки – это принадлежность страны к континенту. Текущая задача решена в скрипте 11_select_some_sorted_columns_where.mq5. После его запуска в журнале увидим такие строки:

11_select_some_sorted_columns_where (EURUSD,H1)  #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY
11_select_some_sorted_columns_where (EURUSD,H1) --+----------------------------------------------------------
11_select_some_sorted_columns_where (EURUSD,H1)  1|        710 South Africa   ZA   Africa            ZAR      
11_select_some_sorted_columns_where (EURUSD,H1)  2|        392 Japan          JP   Asia              JPY      
11_select_some_sorted_columns_where (EURUSD,H1)  3|        410 South Korea    KR   Asia              KRW      
11_select_some_sorted_columns_where (EURUSD,H1)  4|        702 Singapore      SG   Asia              SGD      
11_select_some_sorted_columns_where (EURUSD,H1)  5|        554 New Zealand    NZ   Australia/Oceania NZD      
11_select_some_sorted_columns_where (EURUSD,H1)  6|        578 Norway         NO   Europe            NOK      
11_select_some_sorted_columns_where (EURUSD,H1)  7|        724 Spain          ES   Europe            EUR      
11_select_some_sorted_columns_where (EURUSD,H1)  8|        752 Sweden         SE   Europe            SEK      
11_select_some_sorted_columns_where (EURUSD,H1)  9|        756 Switzerland    CH   Europe            CHF      
11_select_some_sorted_columns_where (EURUSD,H1) 10|        826 United Kingdom GB   Europe            GBP      
11_select_some_sorted_columns_where (EURUSD,H1) 11|        484 Mexico         MX   North America     MXN      
11_select_some_sorted_columns_where (EURUSD,H1) 12|        840 United States  US   North America     USD      


 В итоге на первое место в выборке попадает страна "South Africa", т.к. континент "Africa" идёт первым по списку континентов.


2.11 Обновление некоторых столбцов таблицы по условию

Допустим, что перед нами стоит задача обновить строки в выбранных столбцах. Причём нужно это сделать, выполнив предварительно какое-то условие.

Давайте возьмём азиатские страны и обнулим для них значения в столбцах "CURRENCY", "CURRENCY_SYMBOL". Эту задачу выполняет скрипт 12_update_some_columns.mq5.

По итогам его выполнения получаем такую таблицу:

12_update_some_columns (EURUSD,H1)       #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY CURRENCY_SYMBOL URL_NAME      
12_update_some_columns (EURUSD,H1)      --+-----------------------------------------------------------------------------------------
12_update_some_columns (EURUSD,H1)       1|        554 New Zealand    NZ   Australia/Oceania NZD      $               new-zealand    
12_update_some_columns (EURUSD,H1)       2|        999 European Union EU   Europe            EUR      €               european-union 
12_update_some_columns (EURUSD,H1)       3|        392 Japan          JP   Asia              None     None            japan          
12_update_some_columns (EURUSD,H1)       4|        124 Canada         CA   North America     CAD      $               canada         
12_update_some_columns (EURUSD,H1)       5|         36 Australia      AU   Australia/Oceania AUD      $               australia      
12_update_some_columns (EURUSD,H1)       6|        156 China          CN   Asia              None     None            china          
12_update_some_columns (EURUSD,H1)       7|        380 Italy          IT   Europe            EUR      €               italy          
12_update_some_columns (EURUSD,H1)       8|        702 Singapore      SG   Asia              None     None            singapore      
12_update_some_columns (EURUSD,H1)       9|        276 Germany        DE   Europe            EUR      €               germany        
12_update_some_columns (EURUSD,H1)      10|        250 France         FR   Europe            EUR      €               france         
12_update_some_columns (EURUSD,H1)      11|         76 Brazil         BR   South America     BRL      R$              brazil         
12_update_some_columns (EURUSD,H1)      12|        484 Mexico         MX   North America     MXN      Mex$            mexico         
12_update_some_columns (EURUSD,H1)      13|        710 South Africa   ZA   Africa            ZAR      R               south-africa   
12_update_some_columns (EURUSD,H1)      14|        344 Hong Kong      HK   Asia              None     None            hong-kong      
12_update_some_columns (EURUSD,H1)      15|        356 India          IN   Asia              None     None            india          
12_update_some_columns (EURUSD,H1)      16|        578 Norway         NO   Europe            NOK      Kr              norway         
12_update_some_columns (EURUSD,H1)      17|        840 United States  US   North America     USD      $               united-states  
12_update_some_columns (EURUSD,H1)      18|        826 United Kingdom GB   Europe            GBP      £               united-kingdom 
12_update_some_columns (EURUSD,H1)      19|        756 Switzerland    CH   Europe            CHF      ₣               switzerland    
12_update_some_columns (EURUSD,H1)      20|        410 South Korea    KR   Asia              None     None            south-korea    
12_update_some_columns (EURUSD,H1)      21|        724 Spain          ES   Europe            EUR      €               spain          
12_update_some_columns (EURUSD,H1)      22|        752 Sweden         SE   Europe            SEK      Kr              sweden         
12_update_some_columns (EURUSD,H1)      23|          0 Worldwide      WW   World             ALL                      worldwide      


2.12 Замена и добавление некоторых строк таблицы

Продолжим работу с таблицами. Теперь попробуем заменить некоторые строки в выбранной таблице.

Пусть для страны “Mexico” в столбце  "CURRENCY_SYMBOL" нужно заменить текущий символ "Mex$" на “Peso mexicano”. Выполнение этой задачи поручим скрипту 13_replace_some_rows.mq5.

В текущем варианте таблицы “COUNTRIES” Мексике соответствует такая запись:

COUNTRY_ID
NAME
CODE
CONTINENT
CURRENCY
CURRENCY_SYMBOL
URL_NAME
484 Mexico
MX
North America
MXN
Mex$


mexico


Для того, чтобы заменить эту строку в таблице, нужно указать какое-то уникальное значение выбранной строки. Иначе SQLite не поймёт, что мы хотим заменить.

Допустим, что таким значением будет наименование страны (столбец “NAME”). Тогда в коде функция замены будет представлена так:

//--- the replaced row for "COUNTRY_NAME=Mexico"
string col_names[]=
  {
   "NAME", "CURRENCY_SYMBOL"
  };
string col_vals[2];
col_vals[0]=::StringFormat("'%s'", "Mexico");
col_vals[1]=::StringFormat("'%s'", "Peso mexicano");
if(!db_obj.Replace(col_names, col_vals))
  {
   db_obj.Close();
   return;
  }


При выполнении скрипта получим такую ошибку:

11_replace_some_rows (EURUSD,H1)        database error, NOT NULL constraint failed: COUNTRIES.COUNTRY_ID
11_replace_some_rows (EURUSD,H1)        CDatabase::Replace: failed with code 5619


Очевидно, что нарушено NOT NULL ограничение. Всё дело в том, что изначально, при создании таблицы, было указано, что столбец COUNTRY_ID не может содержать нулевое значение. Значит, нужно обязательно добавить значение для этого столбца. И чтобы не получить полупустую строку, добавим значения для всех столбцов.

//--- the replaced row for "COUNTRY_NAME=Mexico"
string col_names[]=
  {
   "COUNTRY_ID", "NAME", "CODE",
   "CONTINENT", "CURRENCY", "CURRENCY_SYMBOL",
   "URL_NAME"
  };
string col_vals[7];
col_vals[0]=::StringFormat("%I64u", 484);
col_vals[1]=::StringFormat("'%s'", "Mexico");
col_vals[2]=::StringFormat("'%s'", "MX");
col_vals[3]=::StringFormat("'%s'", "North America");
col_vals[4]=::StringFormat("'%s'", "MXN");
col_vals[5]=::StringFormat("'%s'", "Peso mexicano");
col_vals[6]=::StringFormat("'%s'", "mexico");
if(!db_obj.Replace(col_names, col_vals))
  {
   db_obj.Close();
   return;
  }

Теперь скрипт отработает вполне нормально. В журнале получим такие записи:

13_replace_some_rows (EURUSD,H1)         'Mexico' row before replacement
13_replace_some_rows (EURUSD,H1)        #| COUNTRY_ID NAME   CODE CONTINENT     CURRENCY CURRENCY_SYMBOL URL_NAME
13_replace_some_rows (EURUSD,H1)        -+-----------------------------------------------------------------------
13_replace_some_rows (EURUSD,H1)        1|        484 Mexico MX   North America MXN      Mex$            mexico   
13_replace_some_rows (EURUSD,H1)        
13_replace_some_rows (EURUSD,H1)         'Mexico' row after replacement
13_replace_some_rows (EURUSD,H1)        #| COUNTRY_ID NAME   CODE CONTINENT     CURRENCY CURRENCY_SYMBOL URL_NAME
13_replace_some_rows (EURUSD,H1)        -+-----------------------------------------------------------------------
13_replace_some_rows (EURUSD,H1)        1|        484 Mexico MX   North America MXN      Peso mexicano   mexico   


Стоит отметить, что если бы не существовало строки, содержащей данные о Мексике, то такая строка была бы просто добавлена. Т.е. операция замены является ещё и операцией добавления строк таблицы.


2.13 Удаление некоторых строк таблицы

Теперь посмотрим, как можно не расширять состав строк таблицы, а сокращать. Для этого создадим скрипт 14_delete_some_rows.mq5, который станет по запросу удалять строки из выбранной таблицы, относящиеся к Азии.

После запуская скрипта распечатаем итоговую таблицу:

14_delete_some_rows (EURUSD,H1)  #| COUNTRY_ID NAME           CODE CONTINENT         CURRENCY CURRENCY_SYMBOL URL_NAME      
14_delete_some_rows (EURUSD,H1) --+-----------------------------------------------------------------------------------------
14_delete_some_rows (EURUSD,H1)  1|        554 New Zealand    NZ   Australia/Oceania NZD      $               new-zealand    
14_delete_some_rows (EURUSD,H1)  2|        999 European Union EU   Europe            EUR      €               european-union 
14_delete_some_rows (EURUSD,H1)  3|        124 Canada         CA   North America     CAD      $               canada         
14_delete_some_rows (EURUSD,H1)  4|         36 Australia      AU   Australia/Oceania AUD      $               australia      
14_delete_some_rows (EURUSD,H1)  5|        380 Italy          IT   Europe            EUR      €               italy          
14_delete_some_rows (EURUSD,H1)  6|        276 Germany        DE   Europe            EUR      €               germany        
14_delete_some_rows (EURUSD,H1)  7|        250 France         FR   Europe            EUR      €               france         
14_delete_some_rows (EURUSD,H1)  8|         76 Brazil         BR   South America     BRL      R$              brazil         
14_delete_some_rows (EURUSD,H1)  9|        710 South Africa   ZA   Africa            ZAR      R               south-africa   
14_delete_some_rows (EURUSD,H1) 10|        578 Norway         NO   Europe            NOK      Kr              norway         
14_delete_some_rows (EURUSD,H1) 11|        840 United States  US   North America     USD      $               united-states  
14_delete_some_rows (EURUSD,H1) 12|        826 United Kingdom GB   Europe            GBP      £               united-kingdom 
14_delete_some_rows (EURUSD,H1) 13|        756 Switzerland    CH   Europe            CHF      ₣               switzerland    
14_delete_some_rows (EURUSD,H1) 14|        724 Spain          ES   Europe            EUR      €               spain          
14_delete_some_rows (EURUSD,H1) 15|        752 Sweden         SE   Europe            SEK      Kr              sweden         
14_delete_some_rows (EURUSD,H1) 16|          0 Worldwide      WW   World             ALL                      worldwide      
14_delete_some_rows (EURUSD,H1) 17|        484 Mexico         MX   North America     MXN      Peso mexicano   mexico         


Строк, относящихся к континенту Азия, не обнаружено.


2.14 Добавление столбцов в таблицу

К операциям с таблицами относится и вполне обычная задача – добавление новых столбцов.

Итак, пусть нам понадобилось расширить нашу таблицу “COUNTRIES” и добавить столбец, содержащий количество макроэкономических событий, которые попадают в календарь.

Выполнение задачи возложим на скрипт 15_add_new_column.mq5.

После выполнения скрипта проверим таблицу (Рис. 5). В ней появился новый столбец “EVENTS_NUM”.


Новый столбец “EVENTS_NUM” в таблице COUNTRIES

Рис.5 Новый столбец “EVENTS_NUM” в таблице COUNTRIES


2.15 Переименование столбцов в таблице

Если нужно переименовать столбец в таблице, то обратимся к методу CDatabase::RenameColumn(const string _curr_name, const string _new_name). В качестве параметров нужно указать текущее имя столбца и новое имя столбца. Скрипт 16_rename_column.mq5 изменит имя столбца  "EVENTS_NUM" на "EVENTS_NUMBER".


Переименованный столбец “EVENTS_NUMBER” в таблице COUNTRIES

Рис.6 Переименованный столбец “EVENTS_NUMBER” в таблице COUNTRIES


Теперь таблица примет такой вид (Рис. 6).


2.16 Объединение строк некоторых столбцов

Допустим, что нужно объединить результаты выборок в одной таблице. Для этих целей подойдёт метод CDatabase::Union(). Выполнение задачи возложим на скрипт 17_union_some_columns.mq5.

Пусть у нас есть 2 таблицы “EUROPEAN_COUNTRIES” и “NORTH_AMERICAN_COUNTRIES”. В первой будут находится европейские страны, а во второй – северо-американские. Предварительно создадим таблицы, чтобы объединить их строки. Причём каждая из таблиц будет являться результирующей выборкой из первой таблицы “COUNTRIES”. В коде это выглядит так:

//--- create 2 tables
   string table1_name, table2_name, sql_request;
   table1_name="EUROPEAN_COUNTRIES";
   table2_name="NORTH_AMERICAN_COUNTRIES";
   sql_request="SELECT COUNTRY_ID AS id, NAME AS name, CURRENCY "
               "as currency FROM COUNTRIES "
               "WHERE CONTINENT='North America'";
   if(!db_obj.CreateTableAs(table2_name, sql_request, true))
      {
      db_obj.Close();
      return;
      }
   db_obj.FinalizeSqlRequest();
   sql_request="SELECT COUNTRY_ID AS id, NAME AS name, CURRENCY "
               "as currency FROM COUNTRIES "
               "WHERE CONTINENT='Europe'";
   if(!db_obj.CreateTableAs(table1_name, sql_request, true))
      {
      db_obj.Close();
      return;
      }
   db_obj.FinalizeSqlRequest();


В ходе работы скрипта получим в журнале такие записи:

16_union_some_columns (EURUSD,H1)        #|  id name           currency
16_union_some_columns (EURUSD,H1)       --+----------------------------
16_union_some_columns (EURUSD,H1)        1| 124 Canada         CAD      
16_union_some_columns (EURUSD,H1)        2| 250 France         EUR      
16_union_some_columns (EURUSD,H1)        3| 276 Germany        EUR      
16_union_some_columns (EURUSD,H1)        4| 380 Italy          EUR      
16_union_some_columns (EURUSD,H1)        5| 484 Mexico         MXN      
16_union_some_columns (EURUSD,H1)        6| 578 Norway         NOK      
16_union_some_columns (EURUSD,H1)        7| 724 Spain          EUR      
16_union_some_columns (EURUSD,H1)        8| 752 Sweden         SEK      
16_union_some_columns (EURUSD,H1)        9| 756 Switzerland    CHF      
16_union_some_columns (EURUSD,H1)       10| 826 United Kingdom GBP      
16_union_some_columns (EURUSD,H1)       11| 840 United States  USD      
16_union_some_columns (EURUSD,H1)       12| 999 European Union EUR      


Результирующая выборка включает страны Европы и Северной Америки.


2.17 Разность выборок

Допустим, что у нас есть 2 выборки. И нужно найти такие записи в первой выборке, которых нет во второй. На помощь тут приходит метод CDatabase::Except().

Возьмём для примера таблицы “COUNTRIES” и “EUROPEAN_COUNTRIES”. И посмотрим, какие страны останутся, если для первой таблицы применить оператор EXCEPT.

Решение задачи закодировано в скрипте 18_except_some_columns.mq5.

В результате выполнения программы в журнал будут выведены такие строки:

18_except_some_columns (EURUSD,H1)      #| COUNTRY_ID NAME          CURRENCY
18_except_some_columns (EURUSD,H1)      -+----------------------------------
18_except_some_columns (EURUSD,H1)      1|          0 Worldwide     ALL      
18_except_some_columns (EURUSD,H1)      2|         36 Australia     AUD      
18_except_some_columns (EURUSD,H1)      3|         76 Brazil        BRL      
18_except_some_columns (EURUSD,H1)      4|        124 Canada        CAD      
18_except_some_columns (EURUSD,H1)      5|        484 Mexico        MXN      
18_except_some_columns (EURUSD,H1)      6|        554 New Zealand   NZD      
18_except_some_columns (EURUSD,H1)      7|        710 South Africa  ZAR      
18_except_some_columns (EURUSD,H1)      8|        840 United States USD      


Легко заметить, что в результате получилась выборка стран, в которую не попали европейские. Азиатских тоже нет. Они были удалены ранее.


2.18  Пересечение выборок

Теперь попробуем найти не то, чем выборки отличаются, а то, что их объединяет. Т.е. стоит задача найти общие строки выборок.

Предварительно обновим таблицу “COUNTRIES” и вернём её к первоначальному виду, куда входили азиатские страны.

Создадим две временных таблицы со столбцами “id”, ”name” и ”currency”. В первую попадут страны, у которых значение в столбце “COUNTRY_ID” не превышает 578, а во вторую – у которых в том же столбце не менее 392.

//--- create temporary tables
string table1_name, table2_name, sql_request;
table1_name="Table1";
table2_name="Table2";
sql_request="SELECT COUNTRY_ID AS id, NAME AS name, CURRENCY "
            "as currency FROM COUNTRIES "
            "WHERE COUNTRY_ID<=578";
if(!db_obj.CreateTableAs(table1_name, sql_request, true, true))
  {
   db_obj.Close();
   return;
  }
db_obj.FinalizeSqlRequest();
//--- print the temporary table
string temp_col_names[]= {"*"};
if(db_obj.SelectTable(table1_name, true))
   if(db_obj.SelectFrom(temp_col_names))
     {
      ::Print("   \nTable #1: ");
      db_obj.PrintSqlRequest();
      db_obj.FinalizeSqlRequest();
     }
sql_request="SELECT COUNTRY_ID AS id, NAME AS name, CURRENCY "
            "as currency FROM COUNTRIES "
            "WHERE COUNTRY_ID>=392";
if(!db_obj.CreateTableAs(table2_name, sql_request, true, true))
  {
   db_obj.Close();
   return;
  }
db_obj.FinalizeSqlRequest();
//--- print the temporary table
if(db_obj.SelectTable(table2_name, true))
   if(db_obj.SelectFrom(temp_col_names))
     {
      ::Print("   \nTable #2: ");
      db_obj.PrintSqlRequest();
      db_obj.FinalizeSqlRequest();
     }

Воспользуемся возможностями метода CDatabase::Intersect() в скрипте 19_intersect_some_columns.mq5. В результате получим такие строки в журнале:

19_intersect_some_columns (EURUSD,H1)   #|  id name        currency
19_intersect_some_columns (EURUSD,H1)   -+-------------------------
19_intersect_some_columns (EURUSD,H1)   1| 392 Japan       JPY      
19_intersect_some_columns (EURUSD,H1)   2| 410 South Korea KRW      
19_intersect_some_columns (EURUSD,H1)   3| 484 Mexico      MXN      
19_intersect_some_columns (EURUSD,H1)   4| 554 New Zealand NZD      
19_intersect_some_columns (EURUSD,H1)   5| 578 Norway      NOK      


Скрипт отработал правильно – мы получили список стран, где у страны минимальное значение по id равно 392, а максимальное - 578.


2.19 Создание представлений

Представление (view) – это своего рода виртуальная таблица. Удобство состоит в том, что можно отобразить данные, выбранные из любой другой таблицы.

Создавать представления будем с помощью методов bool CDatabase::CreateView() и bool CDatabase::CreateViewWhere(). Первый создаёт какое-то безусловное представление, а второй – по указанному условию.

Рассмотрим такой пример. У нас есть таблица “COUNTRIES”. Пусть нужно отобрать в новую виртуальную таблицу все страны по столбцам “NAME”, “CONTINENT”, “CURRENCY”.

Решим это задание с помощью скрипта 20_create_view.mq5. На выходе получим такой результат - представление “All_countries” (Рис. 7).


Представление “All_countries”

Рис.7 Представление “All_countries” 


Усложним пример и отберём теперь только европейские страны. За это дело возьмётся скрипт 21_create_view_where.mq5. В итоге у нас появилась виртуальная таблица, содержащая только европейские страны (Рис.8).


Представление “European”

Рис.8 Представление “European”


С одной стороны представления не являются полноценными таблицами - в них нельзя добавлять, удалять или обновлять строки, а с другой - с их помощью можно в удобном виде агрегировать результаты сложных запросов, выбирать отдельные столбцы, меняя имена, не затрагивая связи между самими таблицами.


2.20 Удаление представлений

Удалить ранее созданное представление можно с помощью метода CDatabase::DropView().

Метод похож на свой аналог, который удаляет таблицы DropTable(). В предыдущих примерах, прежде чем создавать представление, вызывался именно метод удаления представления.

Скажем несколько слов о конструкции IF EXISTS. Если пробовать удалить несуществующее представление с этой конструкцией, то метод вернёт true, иначе – false.

Посмотрим, как работает скрипт 22_drop_view.mq5.

//--- drop a view
string table_name="COUNTRIES";
if(db_obj.SelectTable(table_name))
   for(int idx=0; idx<2; idx++)
     {
      string view_name=::StringFormat("European%d", idx+1);
      bool if_exists=idx;
      if(db_obj.DropView(view_name, if_exists))
         ::PrintFormat("A view \"%s\" has been successfully dropped!", view_name);
      db_obj.FinalizeSqlRequest();
     }

Сначала он пытается удалить несуществующее представление "European_countries1" без обращения к «IF EXISTS». В результате чего получаем ошибку 5601:

22_drop_view (EURUSD,H1)        database error, no such view: European1
22_drop_view (EURUSD,H1)        CDatabase::Select: failed with code 5601
22_drop_view (EURUSD,H1)        A view "European2" has been successfully dropped!


После этого скрипт пытается удалить тоже несуществующее представление "European_countries2", но уже с использованием «IF EXISTS». Удаление второго представления будет успешным, хотя фактически никакого удаления и не было.


2.21 Переименование таблицы

Допустим, что перед нами стоит задача переименовать саму таблицу. Для этого обратимся к методу CDatabase::RenameTable(). Скрипт 23_rename_table.mq5 выполнит команду по переименованию.


Переименованная таблица COUNTRIES1

Рис.9 Переименованная таблица COUNTRIES1


В результате текущая таблица станет называться “COUNTRIES1” (Рис.9).


3. База данных макроэкономических событий

В этом разделе предлагаю заняться созданием реляционной БД макроэкономических событий, которые освещаются в Календаре.

Итак, для начала создадим схему таблиц, из которых будет состоять будущая БД. Отмечу, что в статье «Рецепты MQL5 – Экономический календарь» уже была представлена связь между отношениями календарных структур. Поэтому в нашем случае для базы данных достаточно легко создать таблицы и построить для них связи.


3.1 Таблицы и реляционные связи

В базе данных всего будет 3 исходных таблицы:

  1. COUNTRIES;
  2. EVENTS;
  3. EVENT_VALUES.

Реляционные связи между таблицами представлены на Рис.10.


Схема связей между таблицами в базе данных Calendar_DB

Рис.10 Схема связей между таблицами в базе данных Calendar_DB


Таблица COUNTRIES станет родительской для таблицы EVENTS. Последняя, в свою очередь, станет дочерней для первой.

Первичным ключом для таблицы COUNTRIES выступает столбец (поле) "COUNTRY_ID". На схеме ему предшествует знак "+". Для таблицы EVENTS таким ключом будет  столбец "EVENT_ID", тогда как столбец "COUNTRY_ID" – это внешний ключ. На схеме ему предшествует символ "#".

Таблица EVENTS станет родительской для таблицы EVENT_VALUES, а вторая станет дочерней для первой.

В таблице EVENT_VALUES первичным ключом выступает столбец (поле) "VALUE_ID", а внешним - "EVENT_ID".

Ключи нужны именно для того, чтобы реализовать обозначенные выше связи между таблицами. А связи, в свою очередь, способствуют целостности данных в базе данных.

Связи между тремя таблицами имеют форму «один ко многим» (1..*). И, наверное, не составит особого труда их расшифровать. Первую связь между странами и событиями можно представить так: у одной страны есть много макроэкономических событий, а у одного события только одна страна. Вторую связь между событиями и событийными значениями можно проиллюстрировать так: у одного события есть много значений, а у любого значения – только одно событие.

Перейдём к коду. В скрипте sCreateAndFillCalendarDB.mq5 реализованы такие этапы:

  1. создание календарной БД;
  2. создание таблиц БД;
  3. заполнение таблиц.

Посмотрим, к примеру, как создаётся таблица EVENTS. Итоговый запрос по созданию этой таблицы будет иметь такой вид:

CREATE TABLE IF NOT EXISTS EVENTS (
    EVENT_ID   [UNSIGNED BIG INT] PRIMARY KEY
                                  NOT NULL,
    TYPE       TEXT,
    SECTOR     TEXT,
    FREQUENCY  TEXT,
    TIME_MODE  TEXT,
    COUNTRY_ID [UNSIGNED BIG INT] NOT NULL,
    UNIT       TEXT,
    IMPORTANCE TEXT,
    MULTIPLIER TEXT,
    DIGITS     [UNSIGNED INT],
    SOURCE     TEXT,
    CODE       TEXT,
    NAME       TEXT,
    FOREIGN KEY (
        COUNTRY_ID
    )
    REFERENCES COUNTRIES (COUNTRY_ID) ON UPDATE CASCADE
                                      ON DELETE CASCADE
)


Особый интерес вызывают строки, где создаётся внешний ключ. Строка FOREIGN KEY (COUNTRY_ID) означает, что в таблице есть внешний ключ по полю COUNTRY_ID. Конструкция REFERENCES COUNTRIES(COUNTRY_ID) используется для указания ссылки на родительскую таблицу COUNTRIES.

Выражения ON UPDATE CASCADE и ON DELETE CASCADE означают, что при удалении или изменении связанной строки из родительской таблицы в дочерней таблице строки также будут удалены или изменены.

Что касается заполнения таблиц, то ниже представлен блок кода, где идёт заполнение таблицы "COUNTRIES".

//--- Table 1
MqlCalendarCountry calendar_countries[];
table_name="COUNTRIES";
if(db_obj.SelectTable(table_name))
   if(db_obj.EmptyTable())
     {
      db_obj.FinalizeSqlRequest();
      string col_names[]=
        {
         "COUNTRY_ID",     // 1
         "NAME",           // 2
         "CODE",           // 3
         "CONTINENT",      // 4
         "CURRENCY",       // 5
         "CURRENCY_SYMBOL",// 6
         "URL_NAME"        // 7
        };
      CiCalendarInfo calendar_info;
      if(calendar_info.Init())
        {
         if(calendar_info.GetCountries(calendar_countries))
           {
            if(db_obj.TransactionBegin())
               for(int c_idx=0; c_idx<::ArraySize(calendar_countries); c_idx++)
                 {
                  MqlCalendarCountry curr_country=calendar_countries[c_idx];
                  string col_vals[];
                  ::ArrayResize(col_vals, 7);
                  col_vals[0]=::StringFormat("%I64u", curr_country.id);
                  col_vals[1]=::StringFormat("'%s'", curr_country.name);
                  col_vals[2]=::StringFormat("'%s'", curr_country.code);
                  col_vals[3]="NULL";
                  SCountryByContinent curr_country_continent_data;
                  if(curr_country_continent_data.Init(curr_country.code))
                     col_vals[3]=::StringFormat("'%s'",
                                                curr_country_continent_data.ContinentDescription());
                  col_vals[4]=::StringFormat("'%s'", curr_country.currency);
                  col_vals[5]=::StringFormat("'%s'", curr_country.currency_symbol);
                  col_vals[6]=::StringFormat("'%s'", curr_country.url_name);
                  if(!db_obj.InsertSingleRow(col_names, col_vals))
                    {
                     db_obj.TransactionRollback();
                     db_obj.Close();
                     return;
                    }
                  db_obj.FinalizeSqlRequest();
                 }
            if(!db_obj.TransactionCommit())
               ::PrintFormat("Failed to complete transaction execution, error %d", ::GetLastError());
           }
         //--- print
         if(db_obj.PrintTable()<0)
            ::PrintFormat("Failed to print the table \"%s\", error %d", table_name, ::GetLastError());
        }
     }


Сначала для дальнейшей работы с таблицей нужно её выбрать с помощью метода CDatabase::SelectTable(). Здесь можно провести аналогию с тем, как выбирается торговая позиция с помощью нативной функции ::PositionSelect() для её дальнейшей обработки.

Затем метод CDatabase::EmptyTable() предварительно очищает таблицу.

Далее в цикле перебираем страны и заполняем таблицу по столбцам

  • "COUNTRY_ID",
  • "COUNTRY_NAME",
  • "COUNTRY_CODE",
  • "CONTINENT",
  • "CURRENCY",
  • "CURRENCY_SYMBOL",
  • "URL_NAME".

Вставка итоговой строки в таблицу осуществляется методом CDatabase::InsertSingleRow(). Отмечу, что тут, при наполнении таблицы, задействован транзакционный механизм. Более подробно об этом можно прочесть в разделе «Ускорение транзакций обертыванием в DatabaseTransactionBegin()/DatabaseTransactionCommit()».

По итогам заполнения трёх таблиц получились следующие результаты: таблица COUNTRIES содержит 23 записи, таблица  EVENTS содержит 1500 записей, а таблица  EVENT_VALUES - 158 696 (Рис.11).


Заполненная таблица EVENT_VALUES

Рис.11 Заполненная таблица EVENT_VALUES


Теперь данные у нас имеются, можно приступать к получению информации - формированию запросов.


3.2 Запросы к базе данных

По большому счёту все запросы к БД можно разделить на 2 группы:

1) запросы, получающие информацию из БД;

2) запросы, изменяющие данные в БД.

Сначала поработаем с примерами получения информации из календарной базы данных.


3.2.1 Выборка количества событий по странам

Давайте для начала спросим у базы данных, сколько макроэкономических событий приходится на каждую страну. Создадим следующий запрос, обратившись к таблице “EVENTS”:

SELECT COUNTRY_ID AS id,
       COUNT(EVENT_ID) AS events_num
  FROM EVENTS
 GROUP BY COUNTRY_ID


В MQL5-коде такой запрос реализован следующим образом:

//--- 1) group events number by country id
string table_name="EVENTS";
if(db_obj.SelectTable(table_name))
  {
   string col_names_to_select[]=
     {
      "COUNTRY_ID AS id", "COUNT(EVENT_ID) AS events_num"
     };
   string gr_names[]=
     {
      "COUNTRY_ID"
     };
   if(!db_obj.SelectFromGroupBy(col_names_to_select, gr_names))
     {
      db_obj.Close();
      return;
     }
//--- print the SQL request
   if(db_obj.PrintSqlRequest()<0)
      ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
   db_obj.FinalizeSqlRequest();


И в журнале терминала на выходе получим такую выборку по исходным столбцам “COUNTRY_ID” и “COUNT(EVENT_ID)”:

sRequest1 (EURUSD,H1)    #|  id events_num
sRequest1 (EURUSD,H1)   --+---------------
sRequest1 (EURUSD,H1)    1|   0          7 
sRequest1 (EURUSD,H1)    2|  36         85 
sRequest1 (EURUSD,H1)    3|  76         55 
sRequest1 (EURUSD,H1)    4| 124         74 
sRequest1 (EURUSD,H1)    5| 156         40 
sRequest1 (EURUSD,H1)    6| 250         43 
sRequest1 (EURUSD,H1)    7| 276         62 
sRequest1 (EURUSD,H1)    8| 344         26 
sRequest1 (EURUSD,H1)    9| 356         57 
sRequest1 (EURUSD,H1)   10| 380         52 
sRequest1 (EURUSD,H1)   11| 392        124 
sRequest1 (EURUSD,H1)   12| 410         36 
sRequest1 (EURUSD,H1)   13| 484         47 
sRequest1 (EURUSD,H1)   14| 554         82 
sRequest1 (EURUSD,H1)   15| 578         47 
sRequest1 (EURUSD,H1)   16| 702         27 
sRequest1 (EURUSD,H1)   17| 710         54 
sRequest1 (EURUSD,H1)   18| 724         39 
sRequest1 (EURUSD,H1)   19| 752         59 
sRequest1 (EURUSD,H1)   20| 756         40 
sRequest1 (EURUSD,H1)   21| 826        115 
sRequest1 (EURUSD,H1)   22| 840        247 
sRequest1 (EURUSD,H1)   23| 999         82 


Согласитесь, не очень читабельно выглядит выборка, т.к. столбец “id” представляет собой идентификатор страны, а не её имя. Но имена стран находятся в другой таблице – “COUNTRIES”.

Чтобы получить имя страны и количество событий страны, нужно создать составной запрос (запрос в запросе).

Первый вариант такого составного запроса выглядит так:

SELECT c.NAME AS country,
       ev.events_num AS events_number
  FROM COUNTRIES c
       JOIN
       (
           SELECT COUNTRY_ID AS id,
                  COUNT(EVENT_ID) AS events_num
             FROM EVENTS
            GROUP BY COUNTRY_ID
       )
       AS ev ON c.COUNTRY_ID = ev.id


В этом варианте используется тот запрос, который мы создали вначале. Но теперь он вошёл в состав другого запроса, тем самым изменив свою форму на форму подзапроса. 

А второй вариант запроса можно реализовать в форме СТЕ:

WITH ev_cnt AS (
    SELECT COUNTRY_ID AS id,
           COUNT(EVENT_ID) AS events_num
      FROM EVENTS
     GROUP BY COUNTRY_ID
)
SELECT c.NAME AS country,
       ev.events_num AS events_number
  FROM COUNTRIES c
       INNER JOIN
       ev_cnt AS ev ON c.COUNTRY_ID = ev.id


В MQL5-коде составной запрос реализован так:

//--- 2) group events number by country name using a subquery
::Print("\nGroup events number by country name using a subquery:\n");
string subquery=db_obj.SqlRequest();
string new_sql_request=::StringFormat("SELECT c.NAME AS country,"
                                      "ev.events_num AS events_number FROM COUNTRIES c "
                                      "JOIN(%s) AS ev "
                                      "ON c.COUNTRY_ID=ev.id", subquery);
if(!db_obj.Select(new_sql_request))
  {
   db_obj.Close();
   return;
  }
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


А табличное выражение (СТЕ) реализовано следующим образом:

//--- 3) group events number by country name using CTE
::Print("\nGroup events number by country name using CTE:\n");
new_sql_request=::StringFormat("WITH ev_cnt AS (%s)"
                               "SELECT c.NAME AS country,"
                               "ev.events_num AS events_number FROM COUNTRIES c "
                               "INNER JOIN ev_cnt AS ev ON c.COUNTRY_ID=ev.id", subquery);
if(!db_obj.Select(new_sql_request))
  {
   db_obj.Close();
   return;
  }
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


Оба варианта распечатают такие результаты запроса в журнале:

sRequest1 (EURUSD,H1)    #| country        events_number
sRequest1 (EURUSD,H1)   --+-----------------------------
sRequest1 (EURUSD,H1)    1| Worldwide                  7 
sRequest1 (EURUSD,H1)    2| Australia                 85 
sRequest1 (EURUSD,H1)    3| Brazil                    55 
sRequest1 (EURUSD,H1)    4| Canada                    74 
sRequest1 (EURUSD,H1)    5| China                     40 
sRequest1 (EURUSD,H1)    6| France                    43 
sRequest1 (EURUSD,H1)    7| Germany                   62 
sRequest1 (EURUSD,H1)    8| Hong Kong                 26 
sRequest1 (EURUSD,H1)    9| India                     57 
sRequest1 (EURUSD,H1)   10| Italy                     52 
sRequest1 (EURUSD,H1)   11| Japan                    124 
sRequest1 (EURUSD,H1)   12| South Korea               36 
sRequest1 (EURUSD,H1)   13| Mexico                    47 
sRequest1 (EURUSD,H1)   14| New Zealand               82 
sRequest1 (EURUSD,H1)   15| Norway                    47 
sRequest1 (EURUSD,H1)   16| Singapore                 27 
sRequest1 (EURUSD,H1)   17| South Africa              54 
sRequest1 (EURUSD,H1)   18| Spain                     39 
sRequest1 (EURUSD,H1)   19| Sweden                    59 
sRequest1 (EURUSD,H1)   20| Switzerland               40 
sRequest1 (EURUSD,H1)   21| United Kingdom           115 
sRequest1 (EURUSD,H1)   22| United States            247 
sRequest1 (EURUSD,H1)   23| European Union            82 


Легко заметить, что больше всего календарь уделяет внимание американским событиям - их в нём 247.

Давайте немного усложним задачу и добавим ещё в выборку столбец, в котором посчитаем, сколько важных событий приходится на ту или иную страну. Степень важности определена в столбце “IMPORTANCE”. Выбирать будем только те события, которые имеют значение “High”.

Сначала поработаем с таблицей “EVENTS”. Здесь нам нужно будет создать 2 выборки. Первая выборка – это подсчёт количества событий по странам. Эту задачу уже выполнили выше. Вторая выборка - подсчёт количества важных событий по странам. И в завершение нужно будет объединить 2 выборки.

SQL-код запроса представлен так:

SELECT evn.COUNTRY_ID AS id,
       COUNT(EVENT_ID) AS events_num,
       imp.high AS imp_events_num
  FROM EVENTS evn
       JOIN
       (
           SELECT COUNTRY_ID AS id,
                  COUNT(IMPORTANCE) AS high
             FROM EVENTS
            WHERE IMPORTANCE = 'High'
            GROUP BY COUNTRY_ID
       )
       AS imp ON evn.COUNTRY_ID = imp.id
 GROUP BY COUNTRY_ID


Что касается реализации на MQL5, то код выглядит следующим образом:

//--- 5) important events - ids, events number and important events number
::Print("\nGroup events number and important events number by country id");
subquery=db_obj.SqlRequest();
string new_sql_request4=::StringFormat("SELECT ev.COUNTRY_ID AS id, COUNT(EVENT_ID) AS events_num,"
                                       "imp.high AS imp_events_num "
                                       "FROM EVENTS ev JOIN (%s) AS imp "
                                       "ON ev.COUNTRY_ID=imp.id GROUP BY COUNTRY_ID", subquery);
if(!db_obj.Select(new_sql_request4))
  {
   db_obj.Close();
   return;
  }
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();

В результате получим такие записи в журнале:

sRequest1 (EURUSD,H1)   Group events number and important events number by country id:
sRequest1 (EURUSD,H1)   
sRequest1 (EURUSD,H1)    #|  id events_num imp_events_num
sRequest1 (EURUSD,H1)   --+------------------------------
sRequest1 (EURUSD,H1)    1|   0          7              2 
sRequest1 (EURUSD,H1)    2|  36         85              5 
sRequest1 (EURUSD,H1)    3|  76         55              2 
sRequest1 (EURUSD,H1)    4| 124         74             10 
sRequest1 (EURUSD,H1)    5| 156         40              5 
sRequest1 (EURUSD,H1)    6| 250         43              1 
sRequest1 (EURUSD,H1)    7| 276         62              3 
sRequest1 (EURUSD,H1)    8| 344         26              1 
sRequest1 (EURUSD,H1)    9| 356         57              2 
sRequest1 (EURUSD,H1)   10| 392        124              7 
sRequest1 (EURUSD,H1)   11| 410         36              2 
sRequest1 (EURUSD,H1)   12| 484         47              2 
sRequest1 (EURUSD,H1)   13| 554         82              8 
sRequest1 (EURUSD,H1)   14| 578         47              2 
sRequest1 (EURUSD,H1)   15| 702         27              1 
sRequest1 (EURUSD,H1)   16| 710         54              2 
sRequest1 (EURUSD,H1)   17| 752         59              1 
sRequest1 (EURUSD,H1)   18| 756         40              4 
sRequest1 (EURUSD,H1)   19| 826        115             13 
sRequest1 (EURUSD,H1)   20| 840        247             20 
sRequest1 (EURUSD,H1)   21| 999         82             11 


В итоговой выборке осталось только заменить столбец “id” на “country”.

Снова создадим составной запрос. Воспользуемся тем, что его части были написаны ранее. И в конце выборку упорядочим по убыванию значений в столбце ‘imp_events_number’. Составной запрос имеет такой вид:

SELECT c.NAME AS country,
       ev.events_num AS events_number,
       ev.imp_events_num AS imp_events_number
  FROM COUNTRIES c
       JOIN
       (
           SELECT ev.COUNTRY_ID AS id,
                  COUNT(EVENT_ID) AS events_num,
                  imp.high AS imp_events_num
             FROM EVENTS ev
                  JOIN
                  (
                      SELECT COUNTRY_ID AS id,
                             COUNT(IMPORTANCE) AS high
                        FROM EVENTS
                       WHERE IMPORTANCE = 'High'
                       GROUP BY COUNTRY_ID
                  )
                  AS imp ON ev.COUNTRY_ID = imp.id
            GROUP BY COUNTRY_ID
       )
       AS ev ON c.COUNTRY_ID = ev.id
 ORDER BY imp_events_number DESC


В MQL5-коде запрос реализован так:

//--- 6) important events - countries, events number and important events number
::Print("\nGroup events number and important events number by country:\n");
subquery=db_obj.SqlRequest();
string new_sql_request5=::StringFormat("SELECT c.NAME AS country,"
                                       "ev.events_num AS events_number,"
                                       "ev.imp_events_num AS imp_events_number "
                                       "FROM COUNTRIES c "
                                       "JOIN(%s) AS ev "
                                       "ON c.COUNTRY_ID=ev.id "
                                       "ORDER BY imp_events_number DESC", subquery);
if(!db_obj.Select(new_sql_request5))
  {
   db_obj.Close();
   return;
  }
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


И теперь в журнале получим искомую выборку:

sRequest1 (EURUSD,H1)   Group events number and important events number by country:
sRequest1 (EURUSD,H1)   
sRequest1 (EURUSD,H1)    #| country        events_number imp_events_number
sRequest1 (EURUSD,H1)   --+-----------------------------------------------
sRequest1 (EURUSD,H1)    1| United States            247                20 
sRequest1 (EURUSD,H1)    2| United Kingdom           115                13 
sRequest1 (EURUSD,H1)    3| European Union            82                11 
sRequest1 (EURUSD,H1)    4| Canada                    74                10 
sRequest1 (EURUSD,H1)    5| New Zealand               82                 8 
sRequest1 (EURUSD,H1)    6| Japan                    124                 7 
sRequest1 (EURUSD,H1)    7| Australia                 85                 5 
sRequest1 (EURUSD,H1)    8| China                     40                 5 
sRequest1 (EURUSD,H1)    9| Switzerland               40                 4 
sRequest1 (EURUSD,H1)   10| Germany                   62                 3 
sRequest1 (EURUSD,H1)   11| Worldwide                  7                 2 
sRequest1 (EURUSD,H1)   12| Brazil                    55                 2 
sRequest1 (EURUSD,H1)   13| India                     57                 2 
sRequest1 (EURUSD,H1)   14| South Korea               36                 2 
sRequest1 (EURUSD,H1)   15| Mexico                    47                 2 
sRequest1 (EURUSD,H1)   16| Norway                    47                 2 
sRequest1 (EURUSD,H1)   17| South Africa              54                 2 
sRequest1 (EURUSD,H1)   18| France                    43                 1 
sRequest1 (EURUSD,H1)   19| Hong Kong                 26                 1 
sRequest1 (EURUSD,H1)   20| Singapore                 27                 1 
sRequest1 (EURUSD,H1)   21| Sweden                    59                 1 


Как видно из выборки, больше всего важных событий у США - 20. На втором месте Великобритания - 13. Третье место занимает Евросоюз - 11. Япония занимает лишь 6-ое место - 7.

И давайте с помощью запроса найдём те страны, у которых вообще нет важных событий. Для этого нужно будет найти разность двух выборок. В первую выборку попадут все страны, которые возьмём из таблицы “COUNTRIES”, а во вторую - столбец со странами из предыдущего составного запроса.

Тогда SQL-код будет таким:

SELECT NAME
  FROM COUNTRIES
EXCEPT
SELECT country
  FROM (
           SELECT c.NAME AS country,
                  ev.events_num AS events_number,
                  ev.imp_events_num AS imp_events_number
             FROM COUNTRIES c
                  JOIN
                  (
                      SELECT ev.COUNTRY_ID AS id,
                             COUNT(EVENT_ID) AS events_num,
                             imp.high AS imp_events_num
                        FROM EVENTS ev
                             JOIN
                             (
                                 SELECT COUNTRY_ID AS id,
                                        COUNT(IMPORTANCE) AS high
                                   FROM EVENTS
                                  WHERE IMPORTANCE = 'High'
                                  GROUP BY COUNTRY_ID
                             )
                             AS imp ON ev.COUNTRY_ID = imp.id
                       GROUP BY COUNTRY_ID
                  )
                  AS ev ON c.COUNTRY_ID = ev.id
       )


MQL5-код будет выглядеть попроще, т.к. воспользуемся тем, что прошлый запрос станет нашим новым подзапросом.

//--- 7) countries having no important events
::Print("\nCountries having no important events:\n");
string last_request=db_obj.SqlRequest();
string new_sql_request6=::StringFormat("SELECT NAME FROM COUNTRIES "
                                       "EXCEPT SELECT country FROM (%s)", last_request);
if(!db_obj.Select(new_sql_request6))
  {
   db_obj.Close();
   return;
  }
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


По завершению выполнения кода получим в журнале такие записи:

sRequest1 (EURUSD,H1)   Countries having no important events:
sRequest1 (EURUSD,H1)   
sRequest1 (EURUSD,H1)   #| NAME 
sRequest1 (EURUSD,H1)   -+------
sRequest1 (EURUSD,H1)   1| Italy 
sRequest1 (EURUSD,H1)   2| Spain 


Т.е. из всех стран только Италия и Испания не имеют важных событий. Запросы о событиях стран на MQL5 выполнялись в скрипте sRequest1.mq5.


3.2.2 Выборка значений ВВП по странам

В этом примере сделаем запрос к базе данных, в результате которого постараемся получить выборку значений ВВП различных стран. В качестве значения ВВП возьмём показатель «Валовой внутренний продукт (ВВП) кв/кв» (за 3-ий квартал).

Выборок будет несколько, поэтому запрос будет составным.

Сначала посмотрим, в экономике каких стран существует поквартальный показатель ВВП.

SQL-код представлен в таком виде:

SELECT COUNTRY_ID,
       EVENT_ID
  FROM EVENTS
 WHERE (NAME LIKE 'GDP q/q' AND 
        SECTOR = 'Gross Domestic Product')


Реализация на MQL5 имеет такой вид (скрипт sRequest2.mq5):

//--- 1) countries by id where the indicator '%GDP q/q%' exists
string col_names[]= {"COUNTRY_ID", "EVENT_ID"};
string where_condition="(NAME LIKE 'GDP q/q' AND SECTOR='Gross Domestic Product')";
if(!db_obj.SelectFromWhere(col_names, where_condition))
  {
   db_obj.Close();
   return;
  }
::Print("\nCountries by id where the indicator 'GDP q/q' exists:\n");
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


И распечатка из журнала после выполнения запроса:

sRequest2 (EURUSD,H1)   Countries by id where the indicator 'GDP q/q' exists:
sRequest2 (EURUSD,H1)   
sRequest2 (EURUSD,H1)    #| COUNTRY_ID  EVENT_ID
sRequest2 (EURUSD,H1)   --+---------------------
sRequest2 (EURUSD,H1)    1|        554 554010024 
sRequest2 (EURUSD,H1)    2|        999 999030016 
sRequest2 (EURUSD,H1)    3|        392 392010001 
sRequest2 (EURUSD,H1)    4|        124 124010022 
sRequest2 (EURUSD,H1)    5|         36  36010019 
sRequest2 (EURUSD,H1)    6|        156 156010004 
sRequest2 (EURUSD,H1)    7|        380 380010020 
sRequest2 (EURUSD,H1)    8|        702 702010004 
sRequest2 (EURUSD,H1)    9|        276 276010008 
sRequest2 (EURUSD,H1)   10|        250 250010005 
sRequest2 (EURUSD,H1)   11|         76  76010010 
sRequest2 (EURUSD,H1)   12|        484 484020016 
sRequest2 (EURUSD,H1)   13|        710 710060009 
sRequest2 (EURUSD,H1)   14|        344 344020002 
sRequest2 (EURUSD,H1)   15|        578 578020012 
sRequest2 (EURUSD,H1)   16|        840 840010007 
sRequest2 (EURUSD,H1)   17|        826 826010037 
sRequest2 (EURUSD,H1)   18|        756 756040001 
sRequest2 (EURUSD,H1)   19|        410 410010011 
sRequest2 (EURUSD,H1)   20|        724 724010005 
sRequest2 (EURUSD,H1)   21|        752 752010019 


Увидим, что искомый показатель существует в 21 стране. Показатель не используется в Индии и как глобальный общемировой ("Worldwide").

Теперь нужно получить выборку значений показателя за 3-ий квартал и связать её с первой выборкой по id события.

SQL-запрос имеет такой вид:

SELECT evs.COUNTRY_ID AS country_id,
       evals.EVENT_ID AS event_id,
       evals.VALUE_ID AS value_id,
       evals.PERIOD AS period,
       evals.TIME AS time,
       evals.ACTUAL AS actual
  FROM EVENT_VALUES evals
       JOIN
       (
           SELECT COUNTRY_ID,
                  EVENT_ID
             FROM EVENTS
            WHERE (NAME LIKE 'GDP q/q' AND 
                   SECTOR = 'Gross Domestic Product') 
       )
       AS evs ON evals.event_id = evs.EVENT_ID
WHERE (period = '2022.07.01 00:00' )


Что касается MQL5-кода, то составной запрос реализован так:

//--- 2)  'GDP y/y' event and last values
string subquery=db_obj.SqlRequest();
string new_sql_request1=::StringFormat("SELECT evs.COUNTRY_ID AS country_id,"
                                       "evals.EVENT_ID AS event_id,"
                                       "evals.VALUE_ID AS value_id,"
                                       "evals.PERIOD AS period,"
                                       "evals.TIME AS time,"
                                       "evals.ACTUAL AS actual "
                                       "FROM EVENT_VALUES evals "
                                       "JOIN(%s) AS evs ON evals.event_id = evs.event_id "
                                       " WHERE (period = \'2022.07.01 00:00\')", subquery);
if(!db_obj.Select(new_sql_request1))
  {
   db_obj.Close();
   return;
  }
::Print("\n'GDP y/y' event and last values:\n");
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


И после выполнения в журнале появятся такие строки:

sRequest2 (EURUSD,H1)   'GDP q/q' event and last values:
sRequest2 (EURUSD,H1)   
sRequest2 (EURUSD,H1)    #| country_id  event_id value_id period           time             actual
sRequest2 (EURUSD,H1)   --+-----------------------------------------------------------------------
sRequest2 (EURUSD,H1)    1|        554 554010024   168293 2022.07.01 00:00 2022.12.14 23:45    2.0 
sRequest2 (EURUSD,H1)    2|        999 999030016   158836 2022.07.01 00:00 2022.10.31 12:00    0.2 
sRequest2 (EURUSD,H1)    3|        999 999030016   158837 2022.07.01 00:00 2022.11.15 12:00    0.2 
sRequest2 (EURUSD,H1)    4|        999 999030016   158838 2022.07.01 00:00 2022.12.07 12:00    0.3 
sRequest2 (EURUSD,H1)    5|        392 392010001   165181 2022.07.01 00:00 2022.11.15 01:50   -0.3 
sRequest2 (EURUSD,H1)    6|        392 392010001   165182 2022.07.01 00:00 2022.12.08 01:50   -0.2 
sRequest2 (EURUSD,H1)    7|        124 124010022   161963 2022.07.01 00:00 2022.11.29 15:30    0.7 
sRequest2 (EURUSD,H1)    8|         36  36010019   173679 2022.07.01 00:00 2022.12.07 02:30    0.6 
sRequest2 (EURUSD,H1)    9|        156 156010004   172459 2022.07.01 00:00 2022.10.24 04:00    3.9 
sRequest2 (EURUSD,H1)   10|        380 380010020   162296 2022.07.01 00:00 2022.10.31 11:00    0.5 
sRequest2 (EURUSD,H1)   11|        380 380010020   162297 2022.07.01 00:00 2022.11.30 11:00    0.5 
sRequest2 (EURUSD,H1)   12|        702 702010004   167581 2022.07.01 00:00 2022.10.14 02:00    1.5 
sRequest2 (EURUSD,H1)   13|        702 702010004   174527 2022.07.01 00:00 2022.11.23 02:00    1.1 
sRequest2 (EURUSD,H1)   14|        276 276010008   172410 2022.07.01 00:00 2022.10.28 10:00    0.3 
sRequest2 (EURUSD,H1)   15|        276 276010008   157759 2022.07.01 00:00 2022.11.25 09:00    0.4 
sRequest2 (EURUSD,H1)   16|        250 250010005   169062 2022.07.01 00:00 2022.10.28 07:30    0.2 
sRequest2 (EURUSD,H1)   17|        250 250010005   169389 2022.07.01 00:00 2022.11.30 09:45    0.2 
sRequest2 (EURUSD,H1)   18|         76  76010010   173825 2022.07.01 00:00 2022.12.01 14:00    0.4 
sRequest2 (EURUSD,H1)   19|        484 484020016   166108 2022.07.01 00:00 2022.10.31 14:00    1.0 
sRequest2 (EURUSD,H1)   20|        484 484020016   166109 2022.07.01 00:00 2022.11.25 14:00    0.9 
sRequest2 (EURUSD,H1)   21|        710 710060009   175234 2022.07.01 00:00 2022.12.06 11:30    1.6 
sRequest2 (EURUSD,H1)   22|        344 344020002   155337 2022.07.01 00:00 2022.10.31 10:30   -2.6 
sRequest2 (EURUSD,H1)   23|        344 344020002   155338 2022.07.01 00:00 2022.11.11 10:30   -2.6 
sRequest2 (EURUSD,H1)   24|        578 578020012   172320 2022.07.01 00:00 2022.11.18 09:00    1.5 
sRequest2 (EURUSD,H1)   25|        840 840010007   163417 2022.07.01 00:00 2022.10.27 14:30    2.6 
sRequest2 (EURUSD,H1)   26|        840 840010007   163418 2022.07.01 00:00 2022.11.30 15:30    2.9 
sRequest2 (EURUSD,H1)   27|        840 840010007   163419 2022.07.01 00:00 2022.12.22 15:30    3.2 
sRequest2 (EURUSD,H1)   28|        826 826010037   157174 2022.07.01 00:00 2022.11.11 09:00   -0.2 
sRequest2 (EURUSD,H1)   29|        826 826010037   157175 2022.07.01 00:00 2022.12.22 09:00   -0.3 
sRequest2 (EURUSD,H1)   30|        756 756040001   159276 2022.07.01 00:00 2022.11.29 10:00    0.2 
sRequest2 (EURUSD,H1)   31|        410 410010011   161626 2022.07.01 00:00 2022.10.27 01:00    0.3 
sRequest2 (EURUSD,H1)   32|        410 410010011   161627 2022.07.01 00:00 2022.12.01 01:00    0.3 
sRequest2 (EURUSD,H1)   33|        724 724010005   159814 2022.07.01 00:00 2022.10.28 09:00    0.2 
sRequest2 (EURUSD,H1)   34|        724 724010005   159815 2022.07.01 00:00 2022.12.23 10:00    0.1 
sRequest2 (EURUSD,H1)   35|        752 752010019   170359 2022.07.01 00:00 2022.10.28 08:00    0.7 
sRequest2 (EURUSD,H1)   36|        752 752010019   171381 2022.07.01 00:00 2022.11.29 09:00    0.6 


Нетрудно заметить, что в выборке по некоторым событиям с одним и тем же event_id есть несколько значений. Например, записи 2-4 относятся к показателю в Евросоюзе. Просто оценка ВВП производилась в нескольких чтениях, поэтому и значений показателя несколько. В итоге в результирующей выборке есть 36 записей, что явно больше количества стран, для которых рассчитывается данный показатель.

Если нужно сделать выборку, получив только последние значения для данного события, то в запрос необходимо внести возможность группировки и фильтр для результатов групп. Тогда получим такой составной SQL-запрос:

SELECT evs.COUNTRY_ID AS country_id,
       evals.EVENT_ID AS event_id,
       evals.VALUE_ID AS value_id,
       evals.PERIOD AS period,
       evals.TIME AS time,
       evals.ACTUAL AS actual
  FROM EVENT_VALUES evals
       JOIN
       (
           SELECT COUNTRY_ID,
                  EVENT_ID
             FROM EVENTS
            WHERE (NAME LIKE 'GDP q/q' AND 
                   SECTOR = 'Gross Domestic Product') 
       )
       AS evs ON evals.event_id = evs.EVENT_ID
WHERE (period = '2022.07.01 00:00' )
GROUP BY evals.event_id 
HAVING MAX(value_id)


Группировать записи будем по столбцу (полю) "event_id". А в качестве основной записи, если будет несколько, возьмём ту, у которой максимальное значение по столбцу (полю) "value_id". Таким образом, например из трёх записей для Евросоюза будет выбрана только одна:

country_id event_id value_id period time actual
999 999030016 158838 2022.07.01 00:00 2022.12.07 12:00 0.3


В итоге в журнале появятся такие записи:

sRequest2 (EURUSD,H1)   'GDP q/q' event and grouped last values:
sRequest2 (EURUSD,H1)   
sRequest2 (EURUSD,H1)    #| country_id  event_id value_id period           time             actual
sRequest2 (EURUSD,H1)   --+-----------------------------------------------------------------------
sRequest2 (EURUSD,H1)    1|         36  36010019   173679 2022.07.01 00:00 2022.12.07 02:30    0.6 
sRequest2 (EURUSD,H1)    2|         76  76010010   173825 2022.07.01 00:00 2022.12.01 14:00    0.4 
sRequest2 (EURUSD,H1)    3|        124 124010022   161963 2022.07.01 00:00 2022.11.29 15:30    0.7 
sRequest2 (EURUSD,H1)    4|        156 156010004   172459 2022.07.01 00:00 2022.10.24 04:00    3.9 
sRequest2 (EURUSD,H1)    5|        250 250010005   169389 2022.07.01 00:00 2022.11.30 09:45    0.2 
sRequest2 (EURUSD,H1)    6|        276 276010008   172410 2022.07.01 00:00 2022.10.28 10:00    0.3 
sRequest2 (EURUSD,H1)    7|        344 344020002   155338 2022.07.01 00:00 2022.11.11 10:30   -2.6 
sRequest2 (EURUSD,H1)    8|        380 380010020   162297 2022.07.01 00:00 2022.11.30 11:00    0.5 
sRequest2 (EURUSD,H1)    9|        392 392010001   165182 2022.07.01 00:00 2022.12.08 01:50   -0.2 
sRequest2 (EURUSD,H1)   10|        410 410010011   161627 2022.07.01 00:00 2022.12.01 01:00    0.3 
sRequest2 (EURUSD,H1)   11|        484 484020016   166109 2022.07.01 00:00 2022.11.25 14:00    0.9 
sRequest2 (EURUSD,H1)   12|        554 554010024   168293 2022.07.01 00:00 2022.12.14 23:45    2.0 
sRequest2 (EURUSD,H1)   13|        578 578020012   172320 2022.07.01 00:00 2022.11.18 09:00    1.5 
sRequest2 (EURUSD,H1)   14|        702 702010004   174527 2022.07.01 00:00 2022.11.23 02:00    1.1 
sRequest2 (EURUSD,H1)   15|        710 710060009   175234 2022.07.01 00:00 2022.12.06 11:30    1.6 
sRequest2 (EURUSD,H1)   16|        724 724010005   159815 2022.07.01 00:00 2022.12.23 10:00    0.1 
sRequest2 (EURUSD,H1)   17|        752 752010019   171381 2022.07.01 00:00 2022.11.29 09:00    0.6 
sRequest2 (EURUSD,H1)   18|        756 756040001   159276 2022.07.01 00:00 2022.11.29 10:00    0.2 
sRequest2 (EURUSD,H1)   19|        826 826010037   157175 2022.07.01 00:00 2022.12.22 09:00   -0.3 
sRequest2 (EURUSD,H1)   20|        840 840010007   163419 2022.07.01 00:00 2022.12.22 15:30    3.2 
sRequest2 (EURUSD,H1)   21|        999 999030016   158838 2022.07.01 00:00 2022.12.07 12:00    0.3 


Теперь в выборке 21 запись. И последний штрих — это заменить код страны на её название. Изменим предыдущий SLQ-запрос на следующий:

SELECT c.NAME AS country,
       ev_evals.event_id AS event_id,
       ev_evals.value_id AS value_id,
       ev_evals.period AS period,
       ev_evals.TIME AS time,
       ev_evals.ACTUAL AS actual
  FROM COUNTRIES c
       JOIN
       (
           SELECT evs.COUNTRY_ID AS country_id,
                  evals.EVENT_ID AS event_id,
                  evals.VALUE_ID AS value_id,
                  evals.PERIOD AS period,
                  evals.TIME AS time,
                  evals.ACTUAL AS actual
             FROM EVENT_VALUES evals
                  JOIN
                  (
                      SELECT COUNTRY_ID,
                             EVENT_ID
                        FROM EVENTS
                       WHERE (NAME LIKE 'GDP q/q' AND 
                              SECTOR = 'Gross Domestic Product') 
                  )
                  AS evs ON evals.event_id = evs.EVENT_ID
            WHERE (period = '2022.07.01 00:00') 
            GROUP BY evals.event_id
           HAVING MAX(value_id) 
       )
       AS ev_evals ON c.COUNTRY_ID = ev_evals.country_id


Попутно реализуем этот составной запрос на MQL5:

//--- 4)  'GDP q/q' event and grouped last values with country names
subquery=db_obj.SqlRequest();
string new_sql_request3=::StringFormat("SELECT c.NAME AS country,"
                                       "ev_evals.event_id AS event_id,"
                                       "ev_evals.value_id AS value_id,"
                                       "ev_evals.period AS period,"
                                       "ev_evals.TIME AS time,"
                                       "ev_evals.ACTUAL AS actual "
                                       "FROM COUNTRIES c JOIN (%s) "
                                       "AS ev_evals ON c.COUNTRY_ID = ev_evals.country_id",
                                       subquery);
if(!db_obj.Select(new_sql_request3))
  {
   db_obj.Close();
   return;
  }
::Print("\n'GDP q/q' event and grouped last values with country names:\n");
//--- print the SQL request
if(db_obj.PrintSqlRequest()<0)
   ::PrintFormat("Failed to print the SQL request, error %d", ::GetLastError());
db_obj.FinalizeSqlRequest();


Искомая выборка будет распечатана в журнале:

sRequest2 (EURUSD,H1)   'GDP q/q' event and grouped last values with country names:
sRequest2 (EURUSD,H1)   
sRequest2 (EURUSD,H1)    #| country         event_id value_id period           time             actual
sRequest2 (EURUSD,H1)   --+---------------------------------------------------------------------------
sRequest2 (EURUSD,H1)    1| Australia       36010019   173679 2022.07.01 00:00 2022.12.07 02:30    0.6 
sRequest2 (EURUSD,H1)    2| Brazil          76010010   173825 2022.07.01 00:00 2022.12.01 14:00    0.4 
sRequest2 (EURUSD,H1)    3| Canada         124010022   161963 2022.07.01 00:00 2022.11.29 15:30    0.7 
sRequest2 (EURUSD,H1)    4| China          156010004   172459 2022.07.01 00:00 2022.10.24 04:00    3.9 
sRequest2 (EURUSD,H1)    5| France         250010005   169389 2022.07.01 00:00 2022.11.30 09:45    0.2 
sRequest2 (EURUSD,H1)    6| Germany        276010008   172410 2022.07.01 00:00 2022.10.28 10:00    0.3 
sRequest2 (EURUSD,H1)    7| Hong Kong      344020002   155338 2022.07.01 00:00 2022.11.11 10:30   -2.6 
sRequest2 (EURUSD,H1)    8| Italy          380010020   162297 2022.07.01 00:00 2022.11.30 11:00    0.5 
sRequest2 (EURUSD,H1)    9| Japan          392010001   165182 2022.07.01 00:00 2022.12.08 01:50   -0.2 
sRequest2 (EURUSD,H1)   10| South Korea    410010011   161627 2022.07.01 00:00 2022.12.01 01:00    0.3 
sRequest2 (EURUSD,H1)   11| Mexico         484020016   166109 2022.07.01 00:00 2022.11.25 14:00    0.9 
sRequest2 (EURUSD,H1)   12| New Zealand    554010024   168293 2022.07.01 00:00 2022.12.14 23:45    2.0 
sRequest2 (EURUSD,H1)   13| Norway         578020012   172320 2022.07.01 00:00 2022.11.18 09:00    1.5 
sRequest2 (EURUSD,H1)   14| Singapore      702010004   174527 2022.07.01 00:00 2022.11.23 02:00    1.1 
sRequest2 (EURUSD,H1)   15| South Africa   710060009   175234 2022.07.01 00:00 2022.12.06 11:30    1.6 
sRequest2 (EURUSD,H1)   16| Spain          724010005   159815 2022.07.01 00:00 2022.12.23 10:00    0.1 
sRequest2 (EURUSD,H1)   17| Sweden         752010019   171381 2022.07.01 00:00 2022.11.29 09:00    0.6 
sRequest2 (EURUSD,H1)   18| Switzerland    756040001   159276 2022.07.01 00:00 2022.11.29 10:00    0.2 
sRequest2 (EURUSD,H1)   19| United Kingdom 826010037   157175 2022.07.01 00:00 2022.12.22 09:00   -0.3 
sRequest2 (EURUSD,H1)   20| United States  840010007   163419 2022.07.01 00:00 2022.12.22 15:30    3.2 
sRequest2 (EURUSD,H1)   21| European Union 999030016   158838 2022.07.01 00:00 2022.12.07 12:00    0.3 


Хотелось бы отметить, что задача пусть и была решена за несколько подходов, но возможность включения одного запроса в состав другого существенно её облегчила.


Заключение

Надеюсь, что статья вызовет интерес у тех трейдеров и разработчиков, кто использует макроэкономические данные при создании своих стратегий. Ещё осмелюсь предположить, что скорее всего нет таких макропоказателей, по которым можно построить хорошую стратегию. Но, например, как добавление к исходным данным нейросетей эти показатели могут быть полезными. 

Прикрепленные файлы |
CalendarDB.zip (52.66 KB)
Как построить советник, работающий автоматически (Часть 03): Новые функции Как построить советник, работающий автоматически (Часть 03): Новые функции
Сегодня вы научитесь создавать советник, который просто и безопасно работает в автоматическом режиме. В предыдущей статье мы начали разрабатывать систему ордеров, которой будем пользоваться в автоматическом советнике. Однако мы создали только одну из необходимых функций или процедур.
Популяционные алгоритмы оптимизации: Алгоритм оптимизации бактериального поиска пищи (Bacterial Foraging Optimization — BFO) Популяционные алгоритмы оптимизации: Алгоритм оптимизации бактериального поиска пищи (Bacterial Foraging Optimization — BFO)
Основа стратегии поиска пищи бактерией E.coli (кишечная палочка) вдохновила ученых на создание алгоритма оптимизации BFO. Алгоритм содержит оригинальные идеи и перспективные подходы к оптимизации и достоин дальнейшего изучения.
Теория категорий в MQL5 (Часть 1) Теория категорий в MQL5 (Часть 1)
Теория категорий представляет собой разнообразный и расширяющийся раздел математики, который пока относительно не освещен в MQL-сообществе. Эта серия статей призвана осветить некоторые из ее концепций для создания открытой библиотеки и дальнейшему использованию этого замечательного раздела в создании торговых стратегий.
Как построить советник, работающий автоматически (Часть 02): Начинаем писать код Как построить советник, работающий автоматически (Часть 02): Начинаем писать код
Сегодня рассмотрим, как создать советник, который просто и безопасно работает в автоматическом режиме. В предыдущей статье я вам представил первые шаги, которые необходимо понять перед тем, как приступать к созданию советника, торгующего автоматически. Мы всё это просмотрели там.