English Русский 中文 Español 日本語 Português
preview
Grafiken in der DoEasy-Bibliothek (Teil 97): Unabhängige Handhabung der Bewegung von Formularobjekten

Grafiken in der DoEasy-Bibliothek (Teil 97): Unabhängige Handhabung der Bewegung von Formularobjekten

MetaTrader 5Beispiele | 27 April 2022, 08:38
175 2
Artyom Trishkin
Artyom Trishkin

Inhalt


Konzept

Während der Entwicklung von erweiterten grafischen Objekten sah ich mich mit der Notwendigkeit konfrontiert, zu Formularobjekten zurückzukehren — Grafische Objekte auf der Leinwand, die in erweiterte grafische Objekte als Kontrollpunkte zur Verwaltung von Ankerpunkten für grafische Objekte implementiert werden sollten. Im vorigen Artikel habe ich die Entwicklung von Mausereignissen für Formularobjekte begonnen. Hier werde ich meine Arbeit an der Handhabung der Bewegung von Formularobjekten beenden. Wir sollten in der Lage sein, jedes Formular im Chart zu verschieben. Die Form, die ausgewählt werden soll, sollte diejenige sein, die vom Mauszeiger gezogen wird, und alle Diagrammwerkzeuge sollten je nach Objekt rechtzeitig korrekt aktiviert und deaktiviert werden.

Lassen Sie uns die Verfolgung von Mausereignissen so einrichten, dass wir die neu entwickelten Handler für die Implementierung aller übrigen Interaktionen eines Formularobjekts mit der Maus verwenden können. Neben der Implementierung des Ziehens der Formulare mit dem Mauszeiger und der Vorbereitung von "Stubs" für andere Interaktionsereignisse werde ich Texte für die Rückgabecodes des Handelsservers und die Fehlercodes der Ausführung hinzufügen. Außerdem werde ich neue Eigenschaften für Handelsobjekte hinzufügen — StopLoss- und TakeProfit-Preise, die schon seit einiger Zeit in den Handelseigenschaften vorhanden sind.


Verbesserung der Klassenbibliothek

In \MQL5\Include\DoEasy\Data.mqh, ergänzen wir die neuen Indices der Nachrichten:

   MSG_LIB_PROP_BID,                                  // Bid price
   MSG_LIB_PROP_ASK,                                  // Ask price
   MSG_LIB_PROP_LAST,                                 // Last deal price
   MSG_LIB_PROP_PRICE_SL,                             // StopLoss price
   MSG_LIB_PROP_PRICE_TP,                             // TakeProfit price
   MSG_LIB_PROP_DEAL_FEE,                             // Deal fee
   MSG_LIB_PROP_PROFIT,                               // Profit
   MSG_LIB_PROP_SYMBOL,                               // Symbol
   MSG_LIB_PROP_BALANCE,                              // Balance operation
   MSG_LIB_PROP_CREDIT,                               // Credit operation
   MSG_LIB_PROP_CLOSE_BY_SL,                          // Closing by StopLoss
   MSG_LIB_PROP_CLOSE_BY_TP,                          // Closing by TakeProfit
   MSG_LIB_PROP_ACCOUNT,                              // Account
   
//--- COrder

und die Texte, der den neu hinzugefügten Indices entsprechen:

   {"Цена Bid","Bid price"},
   {"Цена Ask","Ask price"},
   {"Цена Last","Last price"},
   {"Цена StopLoss","StopLoss price"},
   {"Цена TakeProfit","TakeProfit price"},
   {"Оплата за проведение сделки","Fee for making a deal"},
   {"Прибыль","Profit"},
   {"Символ","Symbol"},
   {"Балансовая операция","Balance operation"},
   {"Кредитная операция","Credit operation"},
   {"Закрытие по StopLoss","Close by StopLoss"},
   {"Закрытие по TakeProfit","Close by TakeProfit"},
   {"Счёт","Account"},
   
//--- COrder

In derselben Datei ergänzen wir die Liste mit den Fehlermeldungen:

//+---------------------------------------------------------------------+
//| Array of messages for trade server return codes (10004 - 10045)     |
//| (1) in user's country language                                      |
//| (2) in the international language                                   |
//+---------------------------------------------------------------------+
string messages_ts_ret_code[][TOTAL_LANG]=
  {
   {"Реквота","Requote"},                                                                                                                          // 10004
   {"Неизвестный код возврата торгового сервера","Unknown trading server return code"},                                                            // 10005
   {"Запрос отклонен","Request rejected"},                                                                                                         // 10006
   {"Запрос отменен трейдером","Request canceled by trader"},                                                                                      // 10007
   {"Ордер размещен","Order placed"},                                                                                                              // 10008
   {"Заявка выполнена","Request completed"},                                                                                                       // 10009
   {"Заявка выполнена частично","Only part of request completed"},                                                                                 // 10010
   {"Ошибка обработки запроса","Request processing error"},                                                                                        // 10011
   {"Запрос отменен по истечению времени","Request canceled by timeout"},                                                                          // 10012
   {"Неправильный запрос","Invalid request"},                                                                                                      // 10013
   {"Неправильный объем в запросе","Invalid volume in request"},                                                                                   // 10014
   {"Неправильная цена в запросе","Invalid price in request"},                                                                                     // 10015
   {"Неправильные стопы в запросе","Invalid stops in request"},                                                                                    // 10016
   {"Торговля запрещена","Trading disabled"},                                                                                                      // 10017
   {"Рынок закрыт","Market closed"},                                                                                                               // 10018
   {"Нет достаточных денежных средств для выполнения запроса","Not enough money to complete request"},                                             // 10019
   {"Цены изменились","Prices changed"},                                                                                                           // 10020
   {"Отсутствуют котировки для обработки запроса","No quotes to process request"},                                                                 // 10021
   {"Неверная дата истечения ордера в запросе","Invalid order expiration date in request"},                                                        // 10022
   {"Состояние ордера изменилось","Order state changed"},                                                                                          // 10023
   {"Слишком частые запросы","Too frequent requests"},                                                                                             // 10024
   {"В запросе нет изменений","No changes in request"},                                                                                            // 10025
   {"Автотрейдинг запрещен сервером","Autotrading disabled by server"},                                                                            // 10026
   {"Автотрейдинг запрещен клиентским терминалом","Autotrading disabled by client terminal"},                                                      // 10027
   {"Запрос заблокирован для обработки","Request locked for processing"},                                                                          // 10028
   {"Ордер или позиция заморожены","Order or position frozen"},                                                                                    // 10029
   {"Указан неподдерживаемый тип исполнения ордера по остатку","Invalid order filling type"},                                                      // 10030
   {"Нет соединения с торговым сервером","No connection with trade server"},                                                                       // 10031
   {"Операция разрешена только для реальных счетов","Operation allowed only for live accounts"},                                                   // 10032
   {"Достигнут лимит на количество отложенных ордеров","Number of pending orders reached limit"},                                                  // 10033
   {"Достигнут лимит на объем ордеров и позиций для данного символа","Volume of orders and positions for symbol reached limit"},                   // 10034
   {"Неверный или запрещённый тип ордера","Incorrect or prohibited order type"},                                                                   // 10035
   {"Позиция с указанным идентификатором уже закрыта","Position with specified identifier already closed"},                                        // 10036
   {"Неизвестный код возврата торгового сервера","Unknown trading server return code"},                                                            // 10037
   {"Закрываемый объем превышает текущий объем позиции","Close volume exceeds the current position volume"},                                       // 10038
   {"Для указанной позиции уже есть ордер на закрытие","Close order already exists for specified position"},                                       // 10039
   {"Достигнут лимит на количество открытых позиций","Number of positions reached limit"},                                                         // 10040
   {
    "Запрос на активацию отложенного ордера отклонен, а сам ордер отменен",                                                                        // 10041
    "The pending order activation request is rejected, the order is canceled"
   },
   {
    "Запрос отклонен, так как на символе установлено правило \"Разрешены только длинные позиции\"",                                                // 10042
    "The request is rejected, because the \"Only long positions are allowed\" rule is set for the symbol"
   },
   {
    "Запрос отклонен, так как на символе установлено правило \"Разрешены только короткие позиции\"",                                               // 10043
    "The request is rejected, because the \"Only short positions are allowed\" rule is set for the symbol"
   },
   {
    "Запрос отклонен, так как на символе установлено правило \"Разрешено только закрывать существующие позиции\"",                                 // 10044
    "The request is rejected, because the \"Only position closing is allowed\" rule is set for the symbol"
   },
   {
    "Запрос отклонен, так как для торгового счета установлено правило \"Разрешено закрывать существующие позиции только по правилу FIFO\"",        // 10045
    "The request is rejected, because \"Position closing is allowed only by FIFO rule\" flag is set for the trading account"
   },
   {
    "Запрос отклонен, так как для торгового счета установлено правило \"Запрещено открывать встречные позиции по одному символу\"",                // 10046
    "The request is rejected, because the \"Opposite positions on a single symbol are disabled\" rule is set for the trading account"
   },
  };
//+------------------------------------------------------------------+

und wir fügen die neuen Arrays — mit den neuen Ausführungsfehlermeldungen, die vorher in der Bibliothek fehlten, aber bereits zu MQL5 hinzugefügt worden sind:

//+------------------------------------------------------------------+
//| Array of execution time error messages (5100 - 5114)             |
//| (Working with OpenCL)                                            |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_opencl[][TOTAL_LANG]=
  {
   {"Функции OpenCL на данном компьютере не поддерживаются","OpenCL functions not supported on this computer"},                                    // 5100
   {"Внутренняя ошибка при выполнении OpenCL","Internal error occurred when running OpenCL"},                                                      // 5101
   {"Неправильный хэндл OpenCL","Invalid OpenCL handle"},                                                                                          // 5102
   {"Ошибка при создании контекста OpenCL","Error creating the OpenCL context"},                                                                   // 5103
   {"Ошибка создания очереди выполнения в OpenCL","Failed to create run queue in OpenCL"},                                                         // 5104
   {"Ошибка при компиляции программы OpenCL","Error occurred when compiling OpenCL program"},                                                      // 5105
   {"Слишком длинное имя точки входа (кернел OpenCL)","Too long kernel name (OpenCL kernel)"},                                                     // 5106
   {"Ошибка создания кернел - точки входа OpenCL","Error creating OpenCL kernel"},                                                                 // 5107
   {
    "Ошибка при установке параметров для кернел OpenCL (точки входа в программу OpenCL)",                                                          // 5108
    "Error occurred when setting parameters for the OpenCL kernel"
   },
   {"Ошибка выполнения программы OpenCL","OpenCL program runtime error"},                                                                          // 5109
   {"Неверный размер буфера OpenCL","Invalid size of OpenCL buffer"},                                                                              // 5110
   {"Неверное смещение в буфере OpenCL","Invalid offset in OpenCL buffer"},                                                                        // 5111
   {"Ошибка создания буфера OpenCL","Failed to create OpenCL buffer"},                                                                             // 5112
   {"Превышено максимальное число OpenCL объектов","Too many OpenCL objects"},                                                                     // 5113
   {"Ошибка выбора OpenCL устройства","OpenCL device selection error"},                                                                            // 5114
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages  (5120 - 5130)            |
//| (Working with databases)                                         |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_database[][TOTAL_LANG]=
  {
   {"Внутренняя ошибка базы данных","Internal database error"},                                                                                    // 5120
   {"Невалидный хендл базы данных","Invalid database handle"},                                                                                     // 5121
   {"Превышено максимально допустимое количество объектов Database","Exceeded the maximum acceptable number of Database objects"},                 // 5122
   {"Ошибка подключения к базе данных","Database connection error"},                                                                               // 5123
   {"Ошибка выполнения запроса","Request execution error"},                                                                                        // 5124
   {"Ошибка создания запроса","Request generation error"},                                                                                         // 5125
   {"Данных для чтения больше нет","No more data to read"},                                                                                        // 5126
   {"Ошибка перехода к следующей записи запроса","Failed to move to the next request entry"},                                                      // 5127
   {"Данные для чтения результатов запроса еще не готовы","Data for reading request results are not ready yet"},                                   // 5128
   {"Ошибка автоподстановки параметров в SQL-запрос","Failed to auto substitute parameters to an SQL request"},                                    // 5129
   {"Запрос базы данных не только для чтения","Database query not read only"},                                                                     // 5130
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages (5200 - 5203)             |
//| (Working with WebRequest())                                      |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_webrequest[][TOTAL_LANG]=
  {
   {"URL не прошел проверку","Invalid URL"},                                                                                                       // 5200
   {"Не удалось подключиться к указанному URL","Failed to connect to specified URL"},                                                              // 5201
   {"Превышен таймаут получения данных","Timeout exceeded"},                                                                                       // 5202
   {"Ошибка в результате выполнения HTTP запроса","HTTP request failed"},                                                                          // 5203
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages (5270 - 5275)             |
//| (Working with network (sockets))                                 |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_netsocket[][TOTAL_LANG]=
  {
   {"В функцию передан неверный хэндл сокета","Invalid socket handle passed to function"},                                                         // 5270
   {"Открыто слишком много сокетов (максимум 128)","Too many open sockets (max 128)"},                                                             // 5271
   {"Ошибка соединения с удаленным хостом","Failed to connect to remote host"},                                                                    // 5272
   {"Ошибка отправки/получения данных из сокета","Failed to send/receive data from socket"},                                                       // 5273
   {"Ошибка установления защищенного соединения (TLS Handshake)","Failed to establish secure connection (TLS Handshake)"},                         // 5274
   {"Отсутствуют данные о сертификате, которым защищено подключение","No data on certificate protecting connection"},                          // 5275
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages (5300 - 5310)             |
//| (Custom symbols)                                                 |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_custom_symbol[][TOTAL_LANG]=
  {
   {"Должен быть указан пользовательский символ","Custom symbol must be specified"},                                                              // 5300
   {"Некорректное имя пользовательского символа","Name of custom symbol invalid"},                                                      // 5301
   {"Слишком длинное имя для пользовательского символа","Name of custom symbol too long"},                                              // 5302
   {"Слишком длинный путь для пользовательского символа","Path of custom symbol too long"},                                             // 5303
   {"Пользовательский символ с таким именем уже существует","Custom symbol with the same name already exists"},                                   // 5304
   {
    "Ошибка при создании, удалении или изменении пользовательского символа",                                                                       // 5305
    "Error occurred while creating, deleting or changing the custom symbol"
   },
   {"Попытка удалить пользовательский символ, выбранный в обзоре рынка","You are trying to delete custom symbol selected in Market Watch"},      // 5306
   {"Неправильное свойство пользовательского символа","Invalid custom symbol property"},                                                         // 5307
   {"Ошибочный параметр при установке свойства пользовательского символа","Wrong parameter while setting property of custom symbol"},            // 5308
   {
    "Слишком длинный строковый параметр при установке свойства пользовательского символа",                                                       // 5309
    "A too long string parameter while setting the property of a custom symbol"
   },
   {"Не упорядоченный по времени массив тиков","Ticks in array not arranged in order of time"},                                        // 5310
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages (5400 - 5402)             |
//| (Economic calendar)                                              |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_calendar[][TOTAL_LANG]=
  {
   {"Размер массива недостаточен для получения описаний всех значений","Array size insufficient for receiving descriptions of all values"},     // 5400
   {"Превышен лимит запроса по времени","Request time limit exceeded"},                                                                            // 5401
   {"Страна не найдена","Country not found"},                                                                                                   // 5402
  };
//+------------------------------------------------------------------+
//| Array of execution time error messages  (5601 - 5626)            |
//| (Working with databases)                                         |
//| (1) in user's country language                                   |
//| (2) in the international language                                |
//+------------------------------------------------------------------+
string messages_runtime_sqlite[][TOTAL_LANG]=
  {
   {"Общая ошибка","Generic error"},                                                                                                               // 5601
   {"Внутренняя логическая ошибка в SQLite","SQLite internal logic error"},                                                                        // 5602
   {"Отказано в доступе","Access denied"},                                                                                                         // 5603
   {"Процедура обратного вызова запросила прерывание","Callback routine requested abort"},                                                         // 5604
   {"Файл базы данных заблокирован","Database file locked"},                                                                                       // 5605
   {"Таблица в базе данных заблокирована ","Database table locked"},                                                                               // 5606
   {"Сбой malloc()","Insufficient memory for completing operation"},                                                                               // 5607
   {"Попытка записи в базу данных, доступной только для чтения ","Attempt to write to readonly database"},                                         // 5608
   {"Операция прекращена с помощью sqlite3_interrupt() ","Operation terminated by sqlite3_interrupt()"},                                           // 5609
   {"Ошибка дискового ввода-вывода","Disk I/O error"},                                                                                             // 5610
   {"Образ диска базы данных испорчен","Database disk image corrupted"},                                                                           // 5611
   {"Неизвестный код операции в sqlite3_file_control()","Unknown operation code in sqlite3_file_control()"},                                       // 5612
   {"Ошибка вставки, так как база данных заполнена ","Insertion failed because database is full"},                                                 // 5613
   {"Невозможно открыть файл базы данных","Unable to open the database file"},                                                                     // 5614
   {"Ошибка протокола блокировки базы данных ","Database lock protocol error"},                                                                    // 5615
   {"Только для внутреннего использования","Internal use only"},                                                                                   // 5616
   {"Схема базы данных изменена","Database schema changed"},                                                                                       // 5617
   {"Строка или BLOB превышает ограничение по размеру","String or BLOB exceeds size limit"},                                                       // 5618
   {"Прервано из-за нарушения ограничения","Abort due to constraint violation"},                                                                   // 5619
   {"Несоответствие типов данных","Data type mismatch"},                                                                                           // 5620
   {"Ошибка неправильного использования библиотеки","Library used incorrectly"},                                                                   // 5621
   {"Использование функций операционной системы, не поддерживаемых на хосте","Uses OS features not supported on host"},                            // 5622
   {"Отказано в авторизации","Authorization denied"},                                                                                              // 5623
   {"Не используется ","Not used "},                                                                                                               // 5624
   {"2-й параметр для sqlite3_bind находится вне диапазона","Bind parameter error, incorrect index"},                                              // 5625
   {"Открытый файл не является файлом базы данных","File opened that is not database file"},                                                       // 5626
  };
//+------------------------------------------------------------------+
#ifdef __MQL4__

Dies sind neue Fehlermeldungen, die in MQL5 eingeführt wurden, nachdem mit der Erstellung der Bibliothek begonnen wurde. Es war nicht möglich, sie in die Bibliothek aufzunehmen, da sie in der Sprache nicht vorhanden waren. Jetzt, da die Fehlercodes implementiert sind und bereits mehrere Terminalversionen veröffentlicht wurden, kann ich diese Fehlercodes endlich ohne das Risiko einer Versionsinkompatibilität in die Bibliothek aufnehmen.

Jetzt ist es an der Zeit, die Nachrichtenklasse der Bibliothek in die Lage zu versetzen, auf diese Klassen zu verweisen, wenn ein Fehlercode behandelt wird.
Um dies zu erreichen, fügen wir in \MQL5\Include\DoEasy\Services\Message.mqh die Codeüberprüfung in Bezug auf die Einhaltung der Wertebereiche hinzu und weisen der Variablen m_text in der Klasse der Fehlermeldungen den String aus dem notwendigen Array zu, der dem Fehlercode entspricht:

//+------------------------------------------------------------------+
//| Get messages from the text array by an ID                        |
//+------------------------------------------------------------------+
void CMessage::GetTextByID(const int msg_id)
  {
   CMessage::m_text=
     (
      //--- Runtime errors (0, 4001 - 4019)
      msg_id==0                     ?  messages_runtime[msg_id][m_lang_num]                       :
     #ifdef __MQL5__
      msg_id>4000 && msg_id<4020    ?  messages_runtime[msg_id-4000][m_lang_num]                  :
      //--- Runtime errors (Charts 4101 - 4116)
      msg_id>4100 && msg_id<4117    ?  messages_runtime_charts[msg_id-4101][m_lang_num]           :
      //--- Runtime errors (Graphical objects 4201 - 4205)
      msg_id>4200 && msg_id<4206    ?  messages_runtime_graph_obj[msg_id-4201][m_lang_num]        :
      //--- Runtime errors (MarketInfo 4301 - 4305)
      msg_id>4300 && msg_id<4306    ?  messages_runtime_market[msg_id-4301][m_lang_num]           :
      //--- Runtime errors (Access to history 4401 - 4407)
      msg_id>4400 && msg_id<4408    ?  messages_runtime_history[msg_id-4401][m_lang_num]          :
      //--- Runtime errors (Global Variables 4501 - 4524)
      msg_id>4500 && msg_id<4525    ?  messages_runtime_global[msg_id-4501][m_lang_num]           :
      //--- Runtime errors (Custom indicators 4601 - 4603)
      msg_id>4600 && msg_id<4604    ?  messages_runtime_custom_indicator[msg_id-4601][m_lang_num] :
      //--- Runtime errors (Account 4701 - 4758)
      msg_id>4700 && msg_id<4759    ?  messages_runtime_account[msg_id-4701][m_lang_num]          :
      //--- Runtime errors (Indicators 4801 - 4812)
      msg_id>4800 && msg_id<4813    ?  messages_runtime_indicator[msg_id-4801][m_lang_num]        :
      //--- Runtime errors (Market depth 4901 - 4904)
      msg_id>4900 && msg_id<4905    ?  messages_runtime_books[msg_id-4901][m_lang_num]            :
      //--- Runtime errors (File operations 5001 - 5027)
      msg_id>5000 && msg_id<5028    ?  messages_runtime_files[msg_id-5001][m_lang_num]            :
      //--- Runtime errors (Converting strings 5030 - 5044)
      msg_id>5029 && msg_id<5045    ?  messages_runtime_string[msg_id-5030][m_lang_num]           :
      //--- Runtime errors (Working with arrays 5050 - 5063)
      msg_id>5049 && msg_id<5064    ?  messages_runtime_array[msg_id-5050][m_lang_num]            :
      //--- Runtime errors (Working with OpenCL 5100 - 5114)
      msg_id>5099 && msg_id<5115    ?  messages_runtime_opencl[msg_id-5100][m_lang_num]           :
      //--- Runtime errors (Working with databases 5120 - 5130)
      msg_id>5119 && msg_id<5131    ?  messages_runtime_database[msg_id-5120][m_lang_num]         :
      //--- Runtime errors (Working with WebRequest() 5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_webrequest[msg_id-5200][m_lang_num]       :
      //--- Runtime errors (Working with network (sockets) 5270 - 5275)
      msg_id>5269 && msg_id<5276    ?  messages_runtime_netsocket[msg_id-5270][m_lang_num]        :
      //--- Runtime errors (Custom symbols 5300 - 5310)
      msg_id>5299 && msg_id<5311    ?  messages_runtime_custom_symbol[msg_id-5300][m_lang_num]    :
      //--- Runtime errors (Economic calendar 5400 - 5402)
      msg_id>5399 && msg_id<5403    ?  messages_runtime_calendar[msg_id-5400][m_lang_num]         :
      //--- Runtime errors (Working with databases 5601 - 5626)
      msg_id>5600 && msg_id<5627    ?  messages_runtime_sqlite[msg_id-5601][m_lang_num]           :
      //--- Trade server return codes (10004 - 10045)
      msg_id>10003 && msg_id<10047  ?  messages_ts_ret_code[msg_id-10004][m_lang_num]             :
     #else // MQL4
      msg_id>0 && msg_id<10         ?  messages_ts_ret_code_mql4[msg_id][m_lang_num]              :
      msg_id>63 && msg_id<66        ?  messages_ts_ret_code_mql4[msg_id-54][m_lang_num]           :
      msg_id>127 && msg_id<151      ?  messages_ts_ret_code_mql4[msg_id-116][m_lang_num]          :
      msg_id<4000                   ?  messages_ts_ret_code_mql4[26][m_lang_num]                  :
      //--- MQL4 runtime errors (4000 - 4030)
      msg_id<4031                   ?  messages_runtime_4000_4030[msg_id-4000][m_lang_num]        :
      //--- MQL4 runtime errors (4050 - 4075)
      msg_id>4049 && msg_id<4076    ?  messages_runtime_4050_4075[msg_id-4050][m_lang_num]        :
      //--- MQL4 runtime errors (4099 - 4112)
      msg_id>4098 && msg_id<4113    ?  messages_runtime_4099_4112[msg_id-4099][m_lang_num]        :
      //--- MQL4 runtime errors (4200 - 4220)
      msg_id>4199 && msg_id<4221    ?  messages_runtime_4200_4220[msg_id-4200][m_lang_num]        :
      //--- MQL4 runtime errors (4250 - 4266)
      msg_id>4249 && msg_id<4267    ?  messages_runtime_4250_4266[msg_id-4250][m_lang_num]        :
      //--- MQL4 runtime errors (5001 - 5029)
      msg_id>5000 && msg_id<5030    ?  messages_runtime_5001_5029[msg_id-5001][m_lang_num]        :
      //--- MQL4 runtime errors (5200 - 5203)
      msg_id>5199 && msg_id<5204    ?  messages_runtime_5200_5203[msg_id-5200][m_lang_num]        :
     #endif 
      //--- Library messages (ERR_USER_ERROR_FIRST)
      msg_id>ERR_USER_ERROR_FIRST-1 ?  messages_library[msg_id-ERR_USER_ERROR_FIRST][m_lang_num]  : 
      messages_library[MSG_LIB_SYS_ERROR_CODE_OUT_OF_RANGE-ERR_USER_ERROR_FIRST][m_lang_num]
     );
  }
//+------------------------------------------------------------------+

Bei der Behandlung des Rückgabecodes des Handelsservers erhöhen wir die obere Grenze des Code-Bereichs um 1, da wir nun eine neue Meldung mit dem Code 10046 haben, die ebenfalls in den Bereich der behandelten Fehlercodes aufgenommen werden sollte.

Jetzt ist die Bibliothek in der Lage, neue Fehlernummern des Handelsservers und während der Laufzeit korrekt zu behandeln. Fügen wir dem Deal-Objekt neue Eigenschaften hinzu. Jetzt haben wir die neue Eigenschaft "Deal fee", und die Eigenschaften StopLoss und TakeProfit sind jetzt auch in Deals enthalten. Fügen wir sie ebenfalls zu den Geschäftseigenschaften hinzu.

In \MQL5\Include\DoEasy\Defines.mqh fügen wir die neue Eigenschaft zur Enumeration der realen Auftragseigenschaften hinzu (die Bibliothek enthält den abstrakten Auftrag, während alle anderen Inhalte, nämlich Geschäfte und Positionen, davon abgeleitet sind):

//+------------------------------------------------------------------+
//| Order, deal, position real properties                            |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // Open price (MQL5 deal price)
   ORDER_PROP_PRICE_CLOSE,                                  // Close price
   ORDER_PROP_SL,                                           // StopLoss price
   ORDER_PROP_TP,                                           // TaleProfit price
   ORDER_PROP_FEE,                                          // Deal fee (DEAL_FEE from ENUM_DEAL_PROPERTY_DOUBLE)
   ORDER_PROP_PROFIT,                                       // Profit
   ORDER_PROP_COMMISSION,                                   // Commission
   ORDER_PROP_SWAP,                                         // Swap
   ORDER_PROP_VOLUME,                                       // Volume
   ORDER_PROP_VOLUME_CURRENT,                               // Unexecuted volume
   ORDER_PROP_PROFIT_FULL,                                  // Profit+commission+swap
   ORDER_PROP_PRICE_STOP_LIMIT,                             // Limit order price when StopLimit order is activated
  };
#define ORDER_PROP_DOUBLE_TOTAL     (12)                    // Total number of real properties
//+------------------------------------------------------------------+

Wir erhöhen die Gesamtzahl der echten Eigenschaften von 11 auf 12

und fügen die Sortierung nach der neuen Eigenschaft zur Enumeration der möglichen Sortierkriterien für die Reihenfolge und den Umgang hinzu:

//+------------------------------------------------------------------+
//| Possible criteria of sorting orders and deals                    |
//+------------------------------------------------------------------+
#define FIRST_ORD_DBL_PROP          (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP)
#define FIRST_ORD_STR_PROP          (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP)
enum ENUM_SORT_ORDERS_MODE
  {
//--- Sort by integer properties
   SORT_BY_ORDER_TICKET = 0,                                // Sort by an order ticket
   SORT_BY_ORDER_MAGIC,                                     // Sort by an order magic number
   SORT_BY_ORDER_TIME_OPEN,                                 // Sort by an order open time in milliseconds
   SORT_BY_ORDER_TIME_CLOSE,                                // Sort by an order close time in milliseconds
   SORT_BY_ORDER_TIME_EXP,                                  // Sort by an order expiration date
   SORT_BY_ORDER_TYPE_FILLING,                              // Sort by execution type by remainder
   SORT_BY_ORDER_TYPE_TIME,                                 // Sort by order lifetime
   SORT_BY_ORDER_STATUS,                                    // Sort by an order status (market order/pending order/deal/balance and credit operation)
   SORT_BY_ORDER_TYPE,                                      // Sort by an order type
   SORT_BY_ORDER_REASON,                                    // Sort by a deal/order/position reason/source
   SORT_BY_ORDER_STATE,                                     // Sort by an order status
   SORT_BY_ORDER_POSITION_ID,                               // Sort by a position ID
   SORT_BY_ORDER_POSITION_BY_ID,                            // Sort by an opposite position ID
   SORT_BY_ORDER_DEAL_ORDER,                                // Sort by the order a deal is based on
   SORT_BY_ORDER_DEAL_ENTRY,                                // Sort by a deal direction – IN, OUT or IN/OUT
   SORT_BY_ORDER_TIME_UPDATE,                               // Sort by position change time in seconds
   SORT_BY_ORDER_TICKET_FROM,                               // Sort by a parent order ticket
   SORT_BY_ORDER_TICKET_TO,                                 // Sort by a derived order ticket
   SORT_BY_ORDER_PROFIT_PT,                                 // Sort by order profit in points
   SORT_BY_ORDER_CLOSE_BY_SL,                               // Sort by the flag of closing an order by StopLoss
   SORT_BY_ORDER_CLOSE_BY_TP,                               // Sort by the flag of closing an order by TakeProfit
   SORT_BY_ORDER_MAGIC_ID,                                  // Sort by an order/position "magic number" ID
   SORT_BY_ORDER_GROUP_ID1,                                 // Sort by the first order/position group ID
   SORT_BY_ORDER_GROUP_ID2,                                 // Sort by the second order/position group ID
   SORT_BY_ORDER_PEND_REQ_ID,                               // Sort by a pending request ID
   SORT_BY_ORDER_DIRECTION,                                 // Sort by direction (Buy, Sell)
//--- Sort by real properties
   SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP,           // Sort by open price
   SORT_BY_ORDER_PRICE_CLOSE,                               // Sort by close price
   SORT_BY_ORDER_SL,                                        // Sort by StopLoss price
   SORT_BY_ORDER_TP,                                        // Sort by TakeProfit price
   SORT_BY_ORDER_FEE,                                       // Sort by deal fee
   SORT_BY_ORDER_PROFIT,                                    // Sort by profit
   SORT_BY_ORDER_COMMISSION,                                // Sort by commission
   SORT_BY_ORDER_SWAP,                                      // Sort by swap
   SORT_BY_ORDER_VOLUME,                                    // Sort by volume
   SORT_BY_ORDER_VOLUME_CURRENT,                            // Sort by unexecuted volume
   SORT_BY_ORDER_PROFIT_FULL,                               // Sort by profit+commission+swap
   SORT_BY_ORDER_PRICE_STOP_LIMIT,                          // Sort by Limit order when StopLimit order is activated
//--- Sort by string properties
   SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP,               // Sort by symbol
   SORT_BY_ORDER_COMMENT,                                   // Sort by comment
   SORT_BY_ORDER_COMMENT_EXT,                               // Sort by custom comment
   SORT_BY_ORDER_EXT_ID                                     // Sort by order ID in an external trading system
  };
//+------------------------------------------------------------------+

Jetzt sind wir in der Lage, die Angebote nach der neuen Eigenschaft zu sortieren.

In der Liste der möglichen Mauszustände relativ zum Formular, passen wir die Konstantennamen der Enumeration ENUM_MOUSE_FORM_STATE leicht an, damit ihre Namen den Mausstatus relativ zum Formular genauer angeben:

//+------------------------------------------------------------------+
//| The list of possible mouse states relative to the form           |
//+------------------------------------------------------------------+
enum ENUM_MOUSE_FORM_STATE
  {
   MOUSE_FORM_STATE_NONE = 0,                         // Undefined state
//--- Outside the form
   MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED,         // The cursor is outside the form, the mouse buttons are not clicked
   MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED,             // The cursor is outside the form, the mouse button (any) is clicked
   MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL,               // The cursor is outside the form, the mouse wheel is being scrolled
//--- Within the form
   MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED,          // The cursor is inside the form, no mouse buttons are clicked
   MOUSE_FORM_STATE_INSIDE_FORM_PRESSED,              // The cursor is inside the form, the mouse button (any) is clicked
   MOUSE_FORM_STATE_INSIDE_FORM_WHEEL,                // The cursor is inside the form, the mouse wheel is being scrolled
//--- Within the window header area
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED,   // The cursor is inside the active area, the mouse buttons are not clicked
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED,       // The cursor is inside the active area,  any mouse button is clicked
   MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL,         // The cursor is inside the active area, the mouse wheel is being scrolled
//--- Within the window scrolling area
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED,   // The cursor is within the window scrolling area, the mouse buttons are not clicked
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED,       // The cursor is within the window scrolling area, the mouse button (any) is clicked
   MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL,         // The cursor is within the window scrolling area, the mouse wheel is being scrolled
  };
//+------------------------------------------------------------------+


Verbessern wir die abstrakte Klasse der Aufträge in \MQL5\Include\DoEasy\Objects\Orders\Order.mqh.

Im geschützten Abschnitt der Klasse deklarieren wir die Methode, die den Wert der Eigenschaft DEAL_FEE aus den Deal-Eigenschaften liest und diesen Wert zurückgibt:

protected:
//--- Protected parametric constructor
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);

//--- Get and return integer properties of a selected order from its parameters
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderState(void)              const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrderTicket(void)         const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderExpiration(void)         const;
   long              PositionTimeUpdateMSC(void)   const;

//--- Get and return real properties of a selected order from its parameters: (1) open price, (2) close price, (3) profit,
//---  (4) commission, (5) swap, (6) volume, (7) unexecuted volume (8) StopLoss price, (9) TakeProfit price (10) StopLimit order price
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            DealFee(void)                 const;
   double            OrderPriceStopLimit(void)     const;

//--- Get and return string properties of a selected order from its parameters: (1) symbol, (2) comment, (3) ID at an exchange
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//--- Return (1) reason, (2) direction, (3) deal type
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;
   
public:


In den Methodenblock für den vereinfachten Zugriff auf die Eigenschaften des Auftragsobjekts fügen wir im öffentlichen Teil der Klasse die Methode hinzu, die den in den Objekteigenschaften festgelegten Wert der Eigenschaft DEAL_FEE zurückgibt:

//+------------------------------------------------------------------+
//| Methods of a simplified access to the order object properties    |
//+------------------------------------------------------------------+
//--- Return (1) ticket, (2) parent order ticket, (3) derived order ticket, (4) magic number, (5) order reason,
//--- (6) position ID, (7) opposite position ID, (8) first group ID, (9) second group ID,
//--- (10) pending request ID, (11) magic number ID, (12) type, (13) flag of closing by StopLoss,
//--- (14) flag of closing by TakeProfit (15) open time, (16) close time,
//--- (17) order expiration date, (18) state, (19) status, (20) type by direction, (21) execution type by remainder, (22) order lifetime
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   long              MagicID(void)                                      const { return this.GetProperty(ORDER_PROP_MAGIC_ID);                   }
   long              GroupID1(void)                                     const { return this.GetProperty(ORDER_PROP_GROUP_ID1);                  }
   long              GroupID2(void)                                     const { return this.GetProperty(ORDER_PROP_GROUP_ID2);                  }
   long              PendReqID(void)                                    const { return this.GetProperty(ORDER_PROP_PEND_REQ_ID);                }
   long              TypeOrder(void)                                    const { return this.GetProperty(ORDER_PROP_TYPE);                       }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   long              TimeOpen(void)                                     const { return this.GetProperty(ORDER_PROP_TIME_OPEN);                  }
   long              TimeClose(void)                                    const { return this.GetProperty(ORDER_PROP_TIME_CLOSE);                 }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_STATE  State(void)                                        const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE);    }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   ENUM_ORDER_TYPE_FILLING TypeFilling(void)                            const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(ORDER_PROP_TYPE_FILLING);  }
   ENUM_ORDER_TYPE_TIME TypeTime(void)                                  const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(ORDER_PROP_TYPE_TIME);        }
   
//--- Return (1) open price, (2) close price, (3) profit, (4) commission, (5) swap, (6) volume, 
//--- (7) unexecuted volume (8) StopLoss and (9) TakeProfit, (10) deal fee and (11) StopLimit order price
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            Fee(void)                                          const { return this.GetProperty(ORDER_PROP_FEE);                        }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
//--- Return (1) symbol, (2) comment, (3) ID at an exchange
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            CommentExt(void)                                   const { return this.GetProperty(ORDER_PROP_COMMENT_EXT);                }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

//--- Get the full order profit
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
//--- Get order profit in points
   int               ProfitInPoints(void) const;
//--- Set (1) the first group ID, (2) the second group ID, (3) the pending request ID, (4) custom comment
   void              SetGroupID1(const long group_id)                         { this.SetProperty(ORDER_PROP_GROUP_ID1,group_id);                }
   void              SetGroupID2(const long group_id)                         { this.SetProperty(ORDER_PROP_GROUP_ID2,group_id);                }
   void              SetPendReqID(const long req_id)                          { this.SetProperty(ORDER_PROP_PEND_REQ_ID,req_id);                }
   void              SetCommentExt(const string comment_ext)                  { this.SetProperty(ORDER_PROP_COMMENT_EXT,comment_ext);           }
   
//+------------------------------------------------------------------+
//| Descriptions of the order object properties                      |
//+------------------------------------------------------------------+


Lesen und Setzen des neuen Eigenschaftswerts auf die Objekteigenschaften im geschlossenen parametrischen Klassenkonstruktor:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Set the object type
   this.m_type=OBJECT_DE_TYPE_ORDER_DEAL_POSITION; 
//--- Save integer properties
   this.m_ticket=ticket;
   this.m_long_prop[ORDER_PROP_STATUS]                               = order_status;
   this.m_long_prop[ORDER_PROP_MAGIC]                                = this.OrderMagicNumber();
   this.m_long_prop[ORDER_PROP_TICKET]                               = this.OrderTicket();
   this.m_long_prop[ORDER_PROP_TIME_EXP]                             = this.OrderExpiration();
   this.m_long_prop[ORDER_PROP_TYPE_FILLING]                         = this.OrderTypeFilling();
   this.m_long_prop[ORDER_PROP_TYPE_TIME]                            = this.OrderTypeTime();
   this.m_long_prop[ORDER_PROP_TYPE]                                 = this.OrderType();
   this.m_long_prop[ORDER_PROP_STATE]                                = this.OrderState();
   this.m_long_prop[ORDER_PROP_DIRECTION]                            = this.OrderTypeByDirection();
   this.m_long_prop[ORDER_PROP_POSITION_ID]                          = this.OrderPositionID();
   this.m_long_prop[ORDER_PROP_REASON]                               = this.OrderReason();
   this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET]                    = this.DealOrderTicket();
   this.m_long_prop[ORDER_PROP_DEAL_ENTRY]                           = this.DealEntry();
   this.m_long_prop[ORDER_PROP_POSITION_BY_ID]                       = this.OrderPositionByID();
   this.m_long_prop[ORDER_PROP_TIME_OPEN]                            = this.OrderOpenTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_CLOSE]                           = this.OrderCloseTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_UPDATE]                          = this.PositionTimeUpdateMSC();
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]         = this.OrderOpenPrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]        = this.OrderClosePrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]             = this.OrderProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]         = this.OrderCommission();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]               = this.OrderSwap();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]             = this.OrderVolume();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SL)]                 = this.OrderStopLoss();
   this.m_double_prop[this.IndexProp(ORDER_PROP_TP)]                 = this.OrderTakeProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_FEE)]                = this.DealFee();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]     = this.OrderVolumeCurrent();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]   = this.OrderPriceStopLimit();
   
//--- Save string properties
   this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]             = this.OrderSymbol();
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]            = this.OrderComment();
   this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]             = this.OrderExternalID();
   
//--- Save additional integer properties
   this.m_long_prop[ORDER_PROP_PROFIT_PT]                            = this.ProfitInPoints();
   this.m_long_prop[ORDER_PROP_TICKET_FROM]                          = this.OrderTicketFrom();
   this.m_long_prop[ORDER_PROP_TICKET_TO]                            = this.OrderTicketTo();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_SL]                          = this.OrderCloseByStopLoss();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_TP]                          = this.OrderCloseByTakeProfit();
   this.m_long_prop[ORDER_PROP_MAGIC_ID]                             = this.GetMagicID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID1]                            = this.GetGroupID1((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID2]                            = this.GetGroupID2((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_PEND_REQ_ID]                          = this.GetPendReqID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   
//--- Save additional real properties
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]        = this.ProfitFull();
   
//--- Save additional string properties
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)]        = "";
  }
//+------------------------------------------------------------------+


In der Methode, die den StopLoss-Kurs zurückgibt, schreiben wir den StopLoss-Wert für den Deal:

//+------------------------------------------------------------------+
//| Return StopLoss price                                            |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_POSITION   : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_SL);    break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Wenn der Auftragsstatus Deal ist, wird der StopLoss-Wert des Deals über sein Ticket gelesen und das Ergebnis zurückgegeben.

Das Gleiche gilt für die Methode, die den TakeProfit-Preis zurückgibt:

//+------------------------------------------------------------------+
//| Return TakeProfit price                                          |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_POSITION   : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_ORDER      :
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_TP);    break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+


Die Methode liefert die Kosten eines Deals:

//+------------------------------------------------------------------+
//| Return the deal fee                                              |
//+------------------------------------------------------------------+
double COrder::DealFee(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   return ::HistoryDealGetDouble(m_ticket,DEAL_FEE);
#endif 
  }
//+------------------------------------------------------------------+

Wenn es sich um MQL4 handelt, hat ein Auftrag keine solche Eigenschaft — es wird Null zurückgegeben.

Im Falle von MQL5, wird der erforderliche Wert aus den Eigenschaften des Geschäfts durch sein Ticket ausgelesen und zurückgegeben.


Wir fügen die Handhabung einer neuen Eigenschaft zu der Methode hinzu, die die Beschreibung der reellen Eigenschaft der Bestellung zurückgibt:

//+------------------------------------------------------------------+
//| Return description of order's real property                      |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- General properties
      property==ORDER_PROP_PRICE_CLOSE       ?  CMessage::Text(MSG_ORD_PRICE_CLOSE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  CMessage::Text(MSG_ORD_PRICE_OPEN)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  CMessage::Text(MSG_LIB_PROP_PRICE_SL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          (this.GetProperty(property)==0     ?  CMessage::Text(MSG_LIB_PROP_EMPTY) : ": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  CMessage::Text(MSG_LIB_PROP_PRICE_TP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          (this.GetProperty(property)==0     ?  CMessage::Text(MSG_LIB_PROP_EMPTY) : ": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_FEE               ?  CMessage::Text(MSG_LIB_PROP_DEAL_FEE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          (this.GetProperty(property)==0     ?  CMessage::Text(MSG_LIB_PROP_EMPTY) : ": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  CMessage::Text(MSG_LIB_PROP_PROFIT)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  CMessage::Text(MSG_ORD_COMMISSION)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  CMessage::Text(MSG_ORD_SWAP)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  CMessage::Text(MSG_ORD_VOLUME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  CMessage::Text(MSG_ORD_VOLUME_CURRENT)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ?  CMessage::Text(MSG_ORD_PRICE_STOP_LIMIT)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- Additional property
      property==ORDER_PROP_PROFIT_FULL       ?  CMessage::Text(MSG_ORD_PROFIT_FULL)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+


Alle anderen Auftragselemente, einschließlich Saldooperationen, Deals und Positionen, werden von der abstrakten Auftragsklasse abgeleitet. Verbessern wir die Deal-Objektklasse in \MQL5\Include\DoEasy\Objects\Orders\HistoryDeal.mqh.

Hier müssen wir die Eigenschaften StopLoss und TakeProfit aus der Liste der nicht unterstützten Eigenschaften in der Methode entfernen, die das Flag zurückgibt, das die Objektunterstützung für die reale Eigenschaft angibt — jetzt verfügt das Geschäft über diese beiden Eigenschaften und sie sollten vom Klassenobjekt unterstützt werden:

//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_TP                || 
      property==ORDER_PROP_SL                || 
      property==ORDER_PROP_PRICE_CLOSE       ||
      property==ORDER_PROP_VOLUME_CURRENT    ||
      property==ORDER_PROP_PRICE_STOP_LIMIT  ||
      (
       this.OrderType()==DEAL_TYPE_BALANCE &&
       (
        property==ORDER_PROP_PRICE_OPEN      ||
        property==ORDER_PROP_COMMISSION      ||
        property==ORDER_PROP_SWAP            ||
        property==ORDER_PROP_VOLUME
       )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Nun sieht die Methode wie folgt aus:

//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_PRICE_CLOSE       ||
      property==ORDER_PROP_VOLUME_CURRENT    ||
      property==ORDER_PROP_PRICE_STOP_LIMIT  ||
      (
       this.OrderType()==DEAL_TYPE_BALANCE &&
       (
        property==ORDER_PROP_PRICE_OPEN      ||
        property==ORDER_PROP_COMMISSION      ||
        property==ORDER_PROP_SWAP            ||
        property==ORDER_PROP_VOLUME
       )
      )
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Da die Methode nicht unterstützte Eigenschaften aufzählt, ist die neu hinzugefügte Eigenschaft DEAL_FEE zunächst nicht in der Liste enthalten, was bedeutet, dass wir sie hier nicht festlegen müssen, da sie standardmäßig unterstützt wird.

Alle neuen Eigenschaften und Fehlernummern werden in die Bibliothek aufgenommen und sollten korrekt behandelt werden.
Nun wollen wir uns dem Ziehen von Formularobjekten mit der Maus widmen.


Unabhängige Handhabung der Bewegung von Formularobjekten

Zunächst sollten wir entscheiden, wie wir das Mausbewegungsereignis behandeln, wenn die Schaltfläche auf dem Formular angeklickt und gehalten wird. In Anbetracht der Erfahrungen aus dem vorherigen Artikel müssen wir ein etwas anderes Konzept für die Behandlung von Mausereignissen in Bezug auf das Formular entwickeln. Beim Testen des EA aus dem vorangegangenen Artikel habe ich mehrere Unzulänglichkeiten und falsches Verhalten bei der Mausinteraktion mit mehreren Formularobjekten festgestellt. Daher brauchen wir einen anderen Ansatz.

Mehrere Experimente haben gezeigt, dass das System der Flags im Handler der Klassenereignisse der grafischen Elemente erstellt werden sollte. Diese Flags sollen anzeigen, an welchem Formularobjekt sich der Mauszeiger gerade befindet, ob die Maustaste gerade gedrückt ist und ob sie außerhalb aller auf dem Chart vorhandenen Formulare gedrückt wurde. Das Formularobjekt enthält das Flag für die Interaktion mit der Umgebung. Verwenden Sie es, um das Formular zu spezifizieren, mit dem Sie arbeiten wollen und dessen Ereignisse Sie behandeln wollen.

Mit anderen Worten, das Konzept sollte wie folgt lauten:

  • Wenn sich der Cursor außerhalb aller Formulare auf dem Chart befindet, kann das Kontextmenü für das Chart aufgerufen werden, ebenso wie das Ziehen mit der Maus und die Verwendung des Fadenkreuz-Werkzeugs, während es keine Reaktion geben sollte, wenn der Cursor die Formulare berührt (mit gedrückter Maustaste außerhalb der Formulare), während wir das Chart mit der Maus ziehen.
  • Sobald der Mauszeiger über einem Formularobjekt schwebt (bei losgelassener Taste), werden alle Chart-Werkzeuge deaktiviert, um auf das Drücken der Maustaste auf dem Formular oder auf eine andere Interaktion der Maus mit dem Formular zu warten (z. B. Scrollen mit dem Mausrad oder Visualisierung des über dem Formular schwebenden Cursors als verschiedene visuelle Formulareffekte).
  • Wenn die Maustaste auf dem Formular gedrückt wird, wird das Interaktions- und Bewegungsflag für das Formular gesetzt. Das Interaktionsflag soll dann dazu dienen, festzulegen, welches der beiden Formulare ausgewählt werden soll, sofern diese beiden Formulare übereinander liegen, der Mauszeiger darüber schwebt und anschließend die Maustaste gedrückt wird. Das Formular mit dem aktiven Interaktionsflag soll ausgewählt werden.
    Wenn wir die Taste loslassen, nachdem wir das Formular mit der Maus gezogen haben, werden die Chart-Werkzeuge aktiviert, während das Interaktionskennzeichen des Formulars aktiv bleibt. Daher wird dieses Formular von den beiden ausgewählt, wenn sie wieder übereinander gelegt werden und die Maustaste losgelassen wird. Wenn der Cursor ein derzeit inaktives Formular auswählt (nicht das, das gerade gezogen wurde) und mit dem Ziehen beginnt, wird die Interaktionsflags vom ersten Formular entfernt und für das ausgewählte Formular aktiviert.

Ein solches Flag-System wird uns immer darüber informieren, welches Formular zuletzt aktiv war, über welchem Formular der Cursor schwebt und ob es visuell hervorgehoben werden kann (diese Handler werden später in der Formularobjektklasse und nicht in der Klasse der grafischen Elemente implementiert). Wir werden immer in der Lage sein, mit einem Formular zu interagieren, über dem sich der Mauszeiger befindet, und ein Formular zu ziehen, das zuletzt mit der Maus ausgewählt wurde.

Wir müssen alles entfernen, was in der Ereignishandhabung der Formularobjektklasse erstellt wurde (mit Ausnahme der Anpassung der Formularkoordinaten im Falle eines Chart-Wechselereignisses). Wir öffnen \MQL5\Include\DoEasy\Objects\Graph\Form.mqh und entfernen den überflüssigen Code aus der Ereignisbehandlung, um es bei der Anpassung der vertikalen Koordinatenverschiebung zu belassen:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CForm::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
  }
//+------------------------------------------------------------------+

Außerdem habe ich die Namen der Enumeration-Konstanten korrigiert, um sie verständlicher zu machen:

//+------------------------------------------------------------------+
//| Return the mouse status relative to the form                     |
//+------------------------------------------------------------------+
ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const int id,const long lparam,const double dparam,const string sparam)
  {
//--- Get the mouse status relative to the form, as well as the states of mouse buttons and Shift/Ctrl keys
   ENUM_MOUSE_FORM_STATE form_state=MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED;
   ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam);
//--- Get the mouse status flags from the CMouseState class object and save them in the variable
   this.m_mouse_state_flags=this.m_mouse.GetMouseFlags();
//--- If the cursor is inside the form
   if(CGCnvElement::CursorInsideElement(m_mouse.CoordX(),m_mouse.CoordY()))
     {
      //--- Set bit 8 responsible for the "cursor inside the form" flag
      this.m_mouse_state_flags |= (0x0001<<8);
      //--- If the cursor is inside the active area, set bit 9 "cursor inside the active area"
      if(CGCnvElement::CursorInsideActiveArea(m_mouse.CoordX(),m_mouse.CoordY()))
         this.m_mouse_state_flags |= (0x0001<<9);
      //--- otherwise, release the bit "cursor inside the active area"
      else this.m_mouse_state_flags &=0xFDFF;
      //--- If one of the mouse buttons is clicked, check the cursor location in the active area and
      //--- return the appropriate value of the pressed key (in the active area or the form area)
      if((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0)
         form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_PRESSED);
      //--- otherwise, if not a single mouse button is pressed
      else
        {
         //--- if the mouse wheel is scrolled, return the appropriate wheel scrolling value (in the active area or the form area)
         if((this.m_mouse_state_flags & 0x0080)!=0)
            form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_FORM_WHEEL);
         //--- otherwise, return the appropriate value of the unpressed key (in the active area or the form area)
         else
            form_state=((this.m_mouse_state_flags & 0x0200)!=0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED);
        } 
     }
//--- If the cursor is outside the form
   else
     {
      //--- return the appropriate button value in an inactive area
      form_state=
        (
         ((this.m_mouse_state_flags & 0x0001)!=0 || (this.m_mouse_state_flags & 0x0002)!=0 || (this.m_mouse_state_flags & 0x0010)!=0) ? 
          MOUSE_FORM_STATE_OUTSIDE_FORM_PRESSED : MOUSE_FORM_STATE_OUTSIDE_FORM_NOT_PRESSED
        );
     }
   return form_state;
  }
//+------------------------------------------------------------------+

Alles ist eingestellt. Jetzt müssen wir das Flag-System in der Ereignishandhabung der Klasse der grafischen Elementsammlung erstellen.

Wir öffnen \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh und nehmen die notwendigen Verbesserungen vor.

Im privaten Abschnitt der Klasse deklarieren wir das Klassenobjekt "Mausstatus" und zwei Methoden — diejenige, die den Zeiger auf das Formular unter dem Cursor zurückgibt und diejenige, die alle Interaktionsflags für alle Formulare außer dem angegebenen zurücksetzt:

//+------------------------------------------------------------------+
//| Collection of graphical objects                                  |
//+------------------------------------------------------------------+
#resource "\\"+PATH_TO_EVENT_CTRL_IND;          // Indicator for controlling graphical object events packed into the program resources
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // List of chart management objects
   CListObj          m_list_all_canv_elm_obj;   // List of all graphical elements on canvas
   CListObj          m_list_all_graph_obj;      // List of all graphical objects
   CArrayObj         m_list_deleted_obj;        // List of removed graphical objects
   CMouseState       m_mouse;                   // "Mouse status" class object
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   
//--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements
   bool              IsPresentCanvElmInList(const long chart_id,const string name);
//--- Return the flag indicating the presence of the graphical object class in the graphical object collection list
   bool              IsPresentGraphObjInList(const long chart_id,const string name);
//--- Return the flag indicating the presence of a graphical object on a chart by name
   bool              IsPresentGraphObjOnChart(const long chart_id,const string name);
//--- Return the pointer to the object of managing objects of the specified chart
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Create a new object of managing graphical objects of a specified chart and add it to the list
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Update the list of graphical objects by chart ID
   CChartObjectsControl *RefreshByChartID(const long chart_id);
//--- Check if the chart window is present
   bool              IsPresentChartWindow(const long chart_id);
//--- Handle removing the chart window
   void              RefreshForExtraObjects(void);
//--- Return the first free ID of the graphical (1) object and (2) element on canvas
   long              GetFreeGraphObjID(bool program_object);
   long              GetFreeCanvElmID(void);
//--- Add (1) the standard graphical object and (2) the graphical element on canvas to the collection
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Return the pointer to the form located under the cursor
   CForm            *GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam,ENUM_MOUSE_FORM_STATE &mouse_state);
//--- Reset all interaction flags for all forms except the specified one
   void              ResetAllInteractionExeptOne(CForm *form);
public:
   bool              AddCanvElmToCollection(CGCnvElement *element);
   
private:
//--- Find an object present in the collection but not on a chart
   CGStdGraphObj    *FindMissingObj(const long chart_id);
   CGStdGraphObj    *FindMissingObj(const long chart_id,int &index);
//--- Find the graphical object present on a chart but not in the collection
   string            FindExtraObj(const long chart_id);
//--- Remove the graphical object class object from the graphical object collection list: (1) specified object, (2) by chart ID
   bool              DeleteGraphObjFromList(CGStdGraphObj *obj);
   void              DeleteGraphObjectsFromList(const long chart_id);
//--- Move the graphical object class object to the list of removed graphical objects: (1) specified object, (2) by index
   bool              MoveGraphObjToDeletedObjList(CGStdGraphObj *obj);
   bool              MoveGraphObjToDeletedObjList(const int index);
//--- Move all objects by chart ID to the list of removed graphical objects
   void              MoveGraphObjectsToDeletedObjList(const long chart_id);
//--- Remove the object of managing charts from the list
   bool              DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
//--- Set the flags of scrolling the chart with the mouse, context menu and crosshairs tool for the specified chart
   void              SetChartTools(const long chart_id,const bool flag);
public:


Die Methode, die den Zeiger auf das unter dem Cursor befindliche Formular zurückgibt:

//+------------------------------------------------------------------+
//| Return the pointer to the form located under the cursor          |
//+------------------------------------------------------------------+
CForm *CGraphElementsCollection::GetFormUnderCursor(const int id, const long &lparam, const double &dparam, const string &sparam,ENUM_MOUSE_FORM_STATE &mouse_state)
  {
//--- Initialize the mouse status relative to the form
   mouse_state=MOUSE_FORM_STATE_NONE;
//--- Declare the pointers to graphical element collection class objects
   CGCnvElement *elm=NULL;
   CForm *form=NULL;
//--- Get the list of objects the interaction flag is set for (there should be only one object)
   CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL);
//--- If managed to obtain the list and it is not empty
   if(list!=NULL && list.Total()>0)
     {
      //--- Get the only graphical element there
      elm=list.At(0);
      //--- If it is a form object
      if(elm.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM)
        {
         //--- Assign the pointer to the element for the form object pointer
         form=elm;
         //--- Get the mouse status relative to the form
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- If the cursor is within the form, return the pointer to the form
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
            return form;
        }
     }
   //--- If there is no a single form object with a specified interaction flag,
   //--- in the loop by all graphical element collection class objects
   int total=this.m_list_all_canv_elm_obj.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the next element
      elm=this.m_list_all_canv_elm_obj.At(i);
      if(elm==NULL)
         continue;
      //--- if the obtained element is a form object
      if(elm.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM)
        {
         //--- Assign the pointer to the element for the form object pointer
         form=elm;
         //--- Get the mouse status relative to the form
         mouse_state=form.MouseFormState(id,lparam,dparam,sparam);
         //--- If the cursor is within the form, return the pointer to the form
         if(mouse_state>MOUSE_FORM_STATE_OUTSIDE_FORM_WHEEL)
            return form;
        }
     }
//--- Nothing is found - return NULL
   return NULL;
  }
//+------------------------------------------------------------------+

Die Methode ist im Programmcode ausführlich kommentiert. Kurz gesagt, wir müssen wissen, auf welchem Formularobjekt sich der Mauszeiger befindet. Wenn wir einfach die Koordinaten des Mauszeigers betrachten und sie mit den Koordinaten und der Größe des Formulars vergleichen, finden wir sicher das Formular, über dem sich der Mauszeiger befindet. Allerdings gibt es hier ein Problem: Wenn zwei Formulare übereinander liegen, wird das Formular ausgewählt, das in der Liste an erster Stelle steht, und nicht das, das sich über allen anderen auf dem Chart befindet, was falsch ist. Daher wird beim Klicken mit der Maustaste auf das Formular das entsprechende Interaktionsflag gesetzt, um das aktive Formular zu finden. Nur wenn ein solches Formular nicht vorhanden ist, beginnen wir mit der Suche nach einem beliebigen aufgelisteten Formular, über dem sich der Mauszeiger befindet. Ein solcher Ansatz sorgt für ein korrektes Verhalten beim Umgang mit der Maus. Es wird immer das Formular ausgewählt, das zuletzt aktiv war. Das ist das Formular, das sich über allen anderen auf dem Chart befindet — also im Vordergrund, da das Formular unmittelbar nach seiner Auswahl in den Vordergrund gerückt wird.

Die Methode setzt die Interaktionsflags für alle Formulare außer dem angegebenen Formular zurück:

//+--------------------------------------------------------------------+
//| Reset all interaction flags for all forms except the specified one |
//+--------------------------------------------------------------------+
void CGraphElementsCollection::ResetAllInteractionExeptOne(CForm *form_exept)
  {
   //--- In the loop by all graphical element collection class objects
   int total=this.m_list_all_canv_elm_obj.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the pointer to a form object
      CForm *form=this.m_list_all_canv_elm_obj.At(i);
      //--- if failed to receive the pointer, or it is not a form, or it is not a form whose pointer has been passed to the method, move on
      if(form==NULL || form.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_FORM || (form.Name()==form_exept.Name() && form.ChartID()==form_exept.ChartID()))
         continue;
      //--- Reset the interaction flag for the current form in the loop
      form.SetInteraction(false);
     }
  }
//+------------------------------------------------------------------+

Mit dieser Methode haben wir immer nur ein Formular mit dem aktiven Interaktionsflag zur gleichen Zeit. Wenn wir ein Formular mit der Maus auswählen, setzen wir sofort das Interaktionsflag für dieses Formular. Für alle anderen Formulare sollte das Flag deaktiviert werden. Dies geschieht beim Aufruf der Methode.

Wir sollten die Ereignisbehandlung der Mausinteraktion mit den Formularen in der Ereignisbehandlung der Klasse der grafischen Elementsammlung bei allen Ereignissen außer dem Chart-Wechselereignis aufrufen. Das Codesegment unmittelbar nach dem chart change event check ist dafür am besten geeignet, da wir auf alle Ereignisse außer diesem reagieren müssen. Platzieren wir den Block der Handler der Mausinteraktion mit den Formularen dort, wo es notwendig ist:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj_std=NULL;  // Pointer to the standard graphical object
   CGCnvElement  *obj_cnv=NULL;  // Pointer to the graphical element object on canvas
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || id==CHARTEVENT_OBJECT_CLICK   ||
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Calculate the chart ID
      //--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID
      //--- If the event ID corresponds to a user event, the chart ID is received from lparam
      //--- Otherwise, the chart ID is assigned to -1
      long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE);
      long chart_id=(param==WRONG_VALUE ? (lparam==0 ? ::ChartID() : lparam) : param);
      //--- Get the object, whose properties were changed or which was relocated,
      //--- from the collection list by its name set in sparam
      obj_std=this.GetStdGraphObject(sparam,chart_id);
      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      if(obj_std==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj_std=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj_std==NULL)
            return;
         //--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         string name_new=this.FindExtraObj(chart_id);
         //--- set a new name for the collection list object, which does not correspond to any graphical object on the chart,
         //--- and send an event with the new name of the object to the control program chart
         if(obj_std.SetNamePrev(obj_std.Name()) && obj_std.SetName(name_new))
            ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj_std.ChartID(),obj_std.TimeCreate(),obj_std.Name());
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj_std.PropertiesRefresh();
      obj_std.PropertiesCheckChanged();
     }

//--- Handle standard graphical object events in the collection list
   for(int i=0;i<this.m_list_all_graph_obj.Total();i++)
     {
      //--- Get the next graphical object and
      obj_std=this.m_list_all_graph_obj.At(i);
      if(obj_std==NULL)
         continue;
      //--- call its event handler
      obj_std.OnChartEvent((id<CHARTEVENT_CUSTOM ? id : idx),lparam,dparam,sparam);
     }

//--- Handle chart changes for extended standard objects
   if(id==CHARTEVENT_CHART_CHANGE || idx==CHARTEVENT_CHART_CHANGE)
     {
      CArrayObj *list=this.GetListStdGraphObjectExt();
      if(list!=NULL)
        {
         for(int i=0;i<list.Total();i++)
           {
            obj_std=list.At(i);
            if(obj_std==NULL)
               continue;
            obj_std.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
           }
        }
     }
//--- Handling mouse events of graphical objects on canvas
//--- If the event is not a chart change
   else
     {
      //--- Check whether the mouse button is pressed
      bool pressed=(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false);
      ENUM_MOUSE_FORM_STATE mouse_state=MOUSE_FORM_STATE_NONE;
      //--- Declare static variables for the active form and status flags
      static CForm *form=NULL;
      static bool pressed_chart=false;
      static bool pressed_form=false;
      static bool move=false;
      
      //--- If the button is not pressed on the chart and the movement flag is not set, get the form, above which the cursor is located
      if(!pressed_chart && !move)
         form=this.GetFormUnderCursor(id,lparam,dparam,sparam,mouse_state);
      
      //--- If the button is not pressed, reset all flags and enable the chart tools 
      if(!pressed)
        {
         pressed_chart=false;
         pressed_form=false;
         move=false;
         SetChartTools(::ChartID(),true);
        }
      
      //--- If this is a mouse movement event and the movement flag is active, move the form, above which the cursor is located (if the pointer to it is valid)
      if(id==CHARTEVENT_MOUSE_MOVE && move)
        {
         if(form!=NULL)
           {
            //--- calculate the cursor movement relative to the form coordinate origin
            int x=this.m_mouse.CoordX()-form.OffsetX();
            int y=this.m_mouse.CoordY()-form.OffsetY();
            //--- get the width and height of the chart the form is located at
            int chart_width=(int)::ChartGetInteger(form.ChartID(),CHART_WIDTH_IN_PIXELS,form.SubWindow());
            int chart_height=(int)::ChartGetInteger(form.ChartID(),CHART_HEIGHT_IN_PIXELS,form.SubWindow());
            //--- Adjust the calculated form coordinates if the form is out of the chart range
            if(x<0) x=0;
            if(x>chart_width-form.Width()) x=chart_width-form.Width();
            if(y<0) y=0;
            if(y>chart_height-form.Height()) y=chart_height-form.Height();
            //--- Move the form by the obtained coordinates
            form.Move(x,y,true);
           }
        }
   
      //--- Display debugging comments on the chart
      Comment
        (
         (form!=NULL ? form.Name()+":" : ""),"\n",
         EnumToString((ENUM_CHART_EVENT)id),"\n",
         EnumToString(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)),
         "\n",EnumToString(mouse_state),
         "\npressed=",pressed,", move=",move,(form!=NULL ? ", Interaction="+(string)form.Interaction() : ""),
         "\npressed_chart=",pressed_chart,", pressed_form=",pressed_form
        );
      
      //--- If the cursor is not above the form
      if(form==NULL)
        {
         //--- If the mouse button is pressed
         if(pressed)
           {
            //--- If the button is still pressed and held on the form, exit
            if(pressed_form)
              {
               return;
              }
            //--- If the button hold flag is not enabled yet, set the flags and enable chart tools
            if(!pressed_chart)
              {
               pressed_chart=true;  // Button is held on the chart
               pressed_form=false;  // Cursor is not above the form
               move=false;          // movement disabled
               SetChartTools(::ChartID(),true);
              }
           }
        }
      //--- If the cursor is above the form
      else
        {
         //--- If the button is still pressed and held on the chart, exit
         if(pressed_chart)
           {
            return;
           }
         
         //--- If the flag of holding the button on the form is not set yet
         if(!pressed_form)
           {
            pressed_chart=false;    // The button is not pressed on the chart
            SetChartTools(::ChartID(),false);
            
            //--- 'The cursor is inside the form, no mouse buttons are clicked' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_NOT_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the form, a mouse button is clicked (any)' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the form, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_FORM_WHEEL)
              {
               
              }
            
            
            //--- 'The cursor is inside the active area, the mouse buttons are not clicked' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED)
              {
               //--- Set the cursor shift relative to the form initial coordinates
               form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX());
               form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY());
              }
            //--- 'The cursor is inside the active area,  any mouse button is clicked' event handler
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && !move)
              {
               pressed_form=true;   // the flag of holding the mouse button on the form
               //--- If the left mouse button is pressed
               if(this.m_mouse.IsPressedButtonLeft())
                 {
                  //--- Set flags and form parameters
                  move=true;                                            // movement flag
                  form.SetInteraction(true);                            // flag of the form interaction with the environment
                  form.BringToTop();                                    // form on the background - above all others
                  this.ResetAllInteractionExeptOne(form);               // Reset interaction flags for all forms except the current one
                  form.SetOffsetX(this.m_mouse.CoordX()-form.CoordX()); // Cursor shift relative to the X coordinate
                  form.SetOffsetY(this.m_mouse.CoordY()-form.CoordY()); // Cursor shift relative to the Y coordinate
                 }
              }
            //--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL)
              {
               
              }
            
            
            //--- 'The cursor is inside the window scrolling area, no mouse buttons are clicked' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_NOT_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the window scrolling area, a mouse button is clicked (any)' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_PRESSED)
              {
               
              }
            //--- 'The cursor is inside the window scrolling area, the mouse wheel is being scrolled' event handler workpiece
            if(mouse_state==MOUSE_FORM_STATE_INSIDE_SCROLL_AREA_WHEEL)
              {
               
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

Der gesamte Codeblock der Ereignisbehandlung 'Maus interagiert mit dem Formular' wird von ausführlichen Kommentaren begleitet. Derzeit haben einige Ereignisse nur "Stubs" — diese Blöcke sollen aufrufende Ereignishandler in einem Formularobjekt enthalten.

Im Moment ist alles bereit, um das neue Konzept zu testen.

Test

Um den Test durchzuführen, verwenden wir den EA aus dem vorherigen Artikel und speichern ihn in \MQL5\Experts\TestDoEasy\Part97\ als TestDoEasyPart97.mq5.

Es wird fast keine Änderungen geben. Ich werde nur die Koordinaten der erstellten Formulare anpassen. Ich werde die zuvor erstellte Makro-Substitution verwenden, die die Anzahl der erstellten Formularobjekte angibt:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart97.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- defines
#define        FORMS_TOTAL (2)   // Number of created forms
#define        START_X     (4)   // Initial X coordinate of the shape
#define        START_Y     (4)   // Initial Y coordinate of the shape
#define KEY_LEFT           (188) // Left
#define KEY_RIGHT          (190) // Right
#define KEY_ORIGIN         (191) // Initial properties
//--- input parameters
sinput   bool              InpMovable     =  true;          // Movable forms flag
sinput   ENUM_INPUT_YES_NO InpUseColorBG  =  INPUT_YES;     // Use chart background color to calculate shadow color
sinput   color             InpColorForm3  =  clrCadetBlue;  // Third form shadow color (if not background color) 
//--- global variables
CEngine        engine;
color          array_clr[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Set EA global variables
   ArrayResize(array_clr,2);        // Array of gradient filling colors
   array_clr[0]=C'26,100,128';      // Original ≈Dark-azure color
   array_clr[1]=C'35,133,169';      // Lightened original color
//--- Create the array with the current symbol and set it to be used in the library
   string array[1]={Symbol()};
   engine.SetUsedSymbols(array);
   //--- Create the timeseries object for the current symbol and period, and show its description in the journal
   engine.SeriesCreate(Symbol(),Period());
   engine.GetTimeSeriesCollection().PrintShort(false); // Short descriptions
//--- Create form objects
   for(int i=0;i<FORMS_TOTAL;i++)
     {
      //--- When creating an object, pass all the required parameters to it
      CForm *form=new CForm("Form_0"+string(i+1),30,(i==0 ? 100 : 160),100,30);
      if(form==NULL)
         continue;
      //--- Set activity and moveability flags for the form
      form.SetActive(true);
      form.SetMovable(true);
      //--- Set the form ID and the index in the list of objects
      form.SetID(i);
      form.SetNumber(0);   // (0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them
      //--- Set the opacity of 200
      form.SetOpacity(245);
      //--- The form background color is set as the first color from the color array
      form.SetColorBackground(array_clr[0]);
      //--- Form outlining frame color
      form.SetColorFrame(clrDarkBlue);
      //--- Draw the shadow drawing flag
      form.SetShadow(false);
      //--- Calculate the shadow color as the chart background color converted to the monochrome one
      color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
      //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units
      //--- Otherwise, use the color specified in the settings for drawing the shadow
      color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
      //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
      //--- Set the shadow opacity to 200, while the blur radius is equal to 4
      form.DrawShadow(3,3,clr,200,4);
      //--- Fill the form background with a vertical gradient
      form.Erase(array_clr,form.Opacity(),true);
      //--- Draw an outlining rectangle at the edges of the form
      form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
      form.Done();
      
      //--- Display the text describing the gradient type and update the form
      //--- Text parameters: the text coordinates and the anchor point in the form center
      //--- Create a new text animation frame with the ID of 0 and display the text on the form
      form.TextOnBG(0,TextByLanguage("Тест 0","Test 0")+string(i+1),form.Width()/2,form.Height()/2,FRAME_ANCHOR_CENTER,C'211,233,149',255,true,true);
      //--- Add the form to the list
      if(!engine.GraphAddCanvElmToCollection(form))
         delete form;
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Kompilieren Sie den EA und starten Sie ihn auf dem Chart:


Ich habe alle genannten Nachteile nach dem Testen des EA aus dem vorherigen Artikel beseitigt. Außerdem ist das Verschieben von Formularen durch die Grenzen des Charts begrenzt. Wenn ein Formular über ein anderes gelegt wird, wird immer das benötigte Formular ausgewählt und die Cursor-Koordinatenverschiebungen relativ zu den verschobenen Formularkoordinaten werden immer korrekt berechnet.

Was kommt als Nächstes?

Im nächsten Artikel werde ich die Entwicklung der grafischen Bibliotheksobjekte fortsetzen.

Alle Dateien der aktuellen Bibliotheksversion, des Test-EA und des Chart-Event-Control-Indikators für MQL5 sind unten zum Testen und Herunterladen angehängt. Stellen Sie Ihre Fragen, Kommentare und Vorschläge bitte im Kommentarteil.

Zurück zum Inhalt

*Frühere Artikel dieser Serie:

Grafiken in der Bibliothek DoEasy (Teil 93): Vorbereiten der Funktionen zur Erstellung zusammengesetzter grafischer Objekte
Grafiken in der Bibliothek DoEasy (Teil 94): Bewegen und Löschen zusammengesetzter grafischer Objekte
Grafiken in der DoEasy-Bibliothek (Teil 95): Steuerelemente für zusammengesetzte grafische Objekte
Grafiken in der DoEasy-Bibliothek (Teil 96): Grafiken in Formularobjekten und Behandlung von Mausereignissen

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/10482

Beigefügte Dateien |
MQL5.zip (4230.01 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (2)
Alexey Viktorov
Alexey Viktorov | 28 Feb. 2022 in 12:17
Vladimir Baskakov #:
Wer braucht sie, außer dem Autor, der diese Artikel wie ein Drucker ausdruckt?

Ich brauche.

Vladimir Gulakov
Vladimir Gulakov | 1 März 2022 in 20:54
EA-Datei testen. Irgendetwas wird in der Tabelle nicht angezeigt. Ich habe versucht, es zu kompilieren, aber es werden 100 Fehler angezeigt. Wäre es zu viel Aufwand für Sie, diese Datei direkt hier einzustellen oder besser noch in KodoBase? Oder vielleicht ist es für mich falsch ausgepackt worden. Generell zum Thema ist eine nützliche Sache, diese Rechtecke so auf das Diagramm zu ziehen. Wir müssen nur hinzufügen, dass man in den Einstellungen die Höhe der Elemente anpassen kann, und wenn diese Elemente auch grafisch auf diesem Rechteck angezeigt werden, wäre das großartig. Außerdem muss sie transparent sein, damit sie das Diagramm nicht verdeckt.
Charts interessanter machen: Hinzufügen eines Hintergrunds Charts interessanter machen: Hinzufügen eines Hintergrunds
Viele Arbeitsplätze enthalten ein repräsentatives Bild, das etwas über den Benutzer aussagt. Diese Bilder machen die Arbeitsumgebung schöner und spannender. Sehen wir uns an, wie man die Charts durch Hinzufügen eines Hintergrunds interessanter gestalten kann.
DirectX-Tutorial (Teil I): Zeichnen des ersten Dreiecks DirectX-Tutorial (Teil I): Zeichnen des ersten Dreiecks
Dies ist ein einführender Artikel über DirectX, der die Besonderheiten der Arbeit mit der API beschreibt. Er soll helfen, die Reihenfolge zu verstehen, in der die Komponenten initialisiert werden. Der Artikel enthält ein Beispiel dafür, wie man ein MQL5-Skript schreibt, das ein Dreieck mit DirectX zeichnet.
Was Sie mit gleitenden Durchschnitten machen können Was Sie mit gleitenden Durchschnitten machen können
In diesem Artikel werden mehrere Methoden zur Anwendung des Indikators Gleitender Durchschnitt (MA oder Moving Average) vorgestellt. Jede Methode, die eine Kurvenanalyse beinhaltet, wird von Indikatoren begleitet, die die Idee visualisieren. In den meisten Fällen stammen die hier vorgestellten Ideen von den jeweiligen Autoren. Meine einzige Aufgabe bestand darin, sie zusammenzubringen, damit Sie die wichtigsten Ansätze sehen und hoffentlich vernünftigere Handelsentscheidungen treffen können. MQL5-Kenntnisstand - einfach.
Lernen Sie, wie Sie ein Handelssystem mit Hilfe des MACD entwickelt Lernen Sie, wie Sie ein Handelssystem mit Hilfe des MACD entwickelt
In diesem Artikel lernen wir ein neues Instrument aus unserer Serie kennen: Wir lernen, wie man ein Handelssystem auf der Grundlage eines der beliebtesten technischen Indikatoren, dem Moving Average Convergence Divergence (MACD), entwickelt.