English Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 76): Новый Chart Trade (III)

Разработка системы репликации (Часть 76): Новый Chart Trade (III)

MetaTrader 5Примеры |
305 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье, "Разработка системы репликации (Часть 75): Новый Chart Trade (II)", мы объясняли различные аспекты класса C_ChartFloatingRAD. Однако из-за плотности материала объяснения давались как можно более подробно, когда это было необходимо. Осталось проанализировать ещё одну процедуру. Даже если бы мы включили этот код в заголовочный файл C_ChartFloatingRAD.mqh и попытались объяснить его в предыдущей статье, сделать это правильно было бы невозможно. Это связано с тем, что для понимания того, как работает процедура DispatchMessage, необходимо объяснить ещё один связанный с ней аспект.

Но в этой статье, мы сможем более подробно рассказать о том, как на самом деле работает процедура DispatchMessage. Данная процедура является самой важной процедурой класса C_ChartFloatingRAD, так как она отвечает за генерацию и реакцию на события, о которых MetaTrader 5 будет сообщать Chart Trade.

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

Давайте теперь перейдем к объяснению процедуры DispatchMessage, которую рассмотрим в следующей теме.


Понимание работы DispatchMessage

Если вы ещё не видели объектов, запрограммированных здесь, в Chart Trade, то, вероятно, вы ещё не поняли, как всё работает в этом контексте. Поэтому прежде, чем продолжить, я предлагаю вам ознакомиться с приведенной выше статьей. Теперь мы рассмотрим то, что делает Chart Trade функциональным, но мы будем делать это только реагируя на события и генерируя их. Пока мы не будем создавать никаких объектов. Мы просто преобразуем события, о которых сообщает MetaTrader 5, в нечто рабочее.

В предыдущей статье, при изучении кода в заголовочном файле C_ChartFloatingRAD.mqh, вы могли заметить, что есть область, где код отсутствует. Отсутствующий код в той же области можно увидеть чуть ниже в следующем выделенном фрагменте:

259. //+------------------------------------------------------------------+
260.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
261.          {
262. #define macro_AdjustMinX(A, B)    {                          \
263.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;   \
264.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;        \
265.             A = (B ? (mx > 0 ? mx : 0) : A);                 \
266.                                  }
267. #define macro_AdjustMinY(A, B)   {                           \
268.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;   \
269.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;        \
270.             A = (B ? (my > 0 ? my : 0) : A);                 \
271.                                  }
272.                               
273.             static short sx = -1, sy = -1, sz = -1;
274.             static eObjectsIDE obj = MSG_NULL;
275.             short   x, y, mx, my;
276.             double dvalue;
277.             bool b1, b2, b3, b4;
278.             ushort ev = evChartTradeCloseAll;
279.    
280.             switch (id)
281.             {
282.                case CHARTEVENT_CHART_CHANGE:
283.                   x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS);
284.                   y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS);
285.                   macro_AdjustMinX(m_Info.x, b1);
286.                   macro_AdjustMinY(m_Info.y, b2);
287.                   macro_AdjustMinX(m_Info.minx, b3);
288.                   macro_AdjustMinY(m_Info.miny, b4);
289.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
290.                   break;
291.                case CHARTEVENT_MOUSE_MOVE:
292.                   if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (CheckMousePosition(x = (short)lparam, y = (short)dparam))
293.                   {
294.                      case MSG_TITLE_IDE:
295.                         if (sx < 0)
296.                         {
297.                            DeleteObjectEdit();
298.                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
299.                            sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
300.                            sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
301.                         }
302.                         if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
303.                         if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
304.                         if (m_Info.IsMaximized)
305.                         {
306.                            m_Info.x = (mx > 0 ? mx : m_Info.x);
307.                            m_Info.y = (my > 0 ? my : m_Info.y);
308.                         }else
309.                         {
310.                            m_Info.minx = (mx > 0 ? mx : m_Info.minx);
311.                            m_Info.miny = (my > 0 ? my : m_Info.miny);
312.                         }
313.                         break;
314.                      case MSG_BUY_MARKET:
315.                         ev = evChartTradeBuy;
316.                      case MSG_SELL_MARKET:
317.                         ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
318.                      case MSG_CLOSE_POSITION:
319.                         if ((m_Info.IsMaximized) && (sz < 0))
320.                         {
321.                            string szTmp = StringFormat("%d?%s?%c?%d?%.2f?%.2f", ev, _Symbol, (m_Info.IsDayTrade ? 'D' : 'S'), m_Info.Leverage, 
322.                                                 FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));
323.                            PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
324.                            sz = x;
325.                            EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
326.                            DeleteObjectEdit();
327.                         }
328.                         break;
329.                   }else
330.                   {
331.                      sz = -1;
332.                      if (sx > 0)
333.                      {
334.                         ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                  
335.                         sx = sy = -1;
336.                      }
337.                   }
338.                   break;
339.                case CHARTEVENT_OBJECT_ENDEDIT:
340.                   switch (obj)
341.                   {
342.                      case MSG_LEVERAGE_VALUE:
343.                      case MSG_TAKE_VALUE:
344.                      case MSG_STOP_VALUE:
345.                         dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
346.                         if (obj == MSG_TAKE_VALUE)
347.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
348.                         else if (obj == MSG_STOP_VALUE)
349.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
350.                         else
351.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
352.                         AdjustTemplate();
353.                         obj = MSG_NULL;
354.                         ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
355.                         break;
356.                   }
357.                   break;
358.                case CHARTEVENT_OBJECT_CLICK:
359.                   if (sparam == m_Info.szObj_Chart) if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (obj = CheckMousePosition(x = (short)lparam, y = (short)dparam))
360.                   {
361.                      case MSG_DAY_TRADE:
362.                         m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
363.                         DeleteObjectEdit();
364.                         break;
365.                      case MSG_MAX_MIN:
366.                         m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
367.                         DeleteObjectEdit();
368.                         break;
369.                      case MSG_LEVERAGE_VALUE:
370.                         CreateObjectEditable(obj, m_Info.Leverage);
371.                         break;
372.                      case MSG_TAKE_VALUE:
373.                         CreateObjectEditable(obj, m_Info.FinanceTake);
374.                         break;
375.                      case MSG_STOP_VALUE:
376.                         CreateObjectEditable(obj, m_Info.FinanceStop);
377.                         break;
378.                   }
379.                   if (obj != MSG_NULL) AdjustTemplate();
380.                   break;
381.                case CHARTEVENT_OBJECT_DELETE:
382.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
383.                   break;
384.             }
385.             ChartRedraw();
386.          }
387. //+------------------------------------------------------------------+
388. };
389. //+------------------------------------------------------------------+

Отсутствующий фрагмент кода C_ChartFloatingRAD.mqh

Обратите внимание, что приведенный выше фрагмент идеально вписывается в недостающую область кода, которую рассматривали в предыдущей статье. Поэтому, чтобы завершить код класса C_ChartFloatingRAD, надо помещать данный фрагмент именно в эту область и следовать перечислению указанных строк. Я не думаю, что это будет сложно для вас. Теперь давайте посмотрим, как это работает.

Первое, что можно заметить в данном фрагменте, - это то, что не было сделано ни одного вызова для создания какого-либо объекта. Как мы уже говорили, на самом деле мы не будем программировать объекты. График торговли создается и сохраняется как шаблон. Это делается полностью внутри MetaTrader 5. В предыдущей статье мы привели несколько ссылок в конце, чтобы вы могли понять концепцию.

Данный тип программирования не получил широкого распространения среди большинства программистов, по крайней мере, по моему опыту. Однако он гораздо практичнее и проще. Это связано с тем, что нет необходимости программировать каждый объект или прилагать усилия для его расположения и настройки. Объект просто упоминается в шаблоне там где нужно. А с точки зрения программирования мы создаем нужный объект только в том случае, если его нельзя настроить непосредственно в MetaTrader 5. Как мы уже объясняли в предыдущей статье, создание элементов и их сохранение в виде шаблона - довольно редкое занятие. Фактически единственным объектом, с которым мы не справились должным образом, был объект OBJ_EDIT. Это связано с тем, что создание всей логики для вставки и управление текстом гораздо сложнее, чем простое создание объекта OBJ_EDIT. Поэтому это единственный объект, который действительно будет создан.

Но давайте вернемся к фрагменту. Вы понимаете, что в данном фрагменте мы управляем всего несколькими событиями? Если всё происходит именно так, то как нам удается делать то, что показывается в видеоролике в предыдущей статье? Даже если видео отсутствует, у вас есть доступ к исполняемым файлам в приложении. Тем не менее, вам, возможно, было непросто понять, как этот код, не содержащий множества объектов, может работать таким образом. Вы вполне могли предположить, что в процедуре DispatchMessage появятся и другие объекты. Однако, если посмотреть на приведенный выше отрывок, то вы увидите, что это не так. ДРУГИХ ОБЪЕКТОВ НЕ СУЩЕСТВУЕТ. Всё, что мы делаем, - это управляем событиями, которые предоставляет нам MetaTrader 5. Как это работает?

Давайте объясним это по частям. Здесь мы обрабатываем пять типов событий, которые отправляет нам MetaTrader 5. Но прежде, чем мы рассмотрим события и их обработку, можно увидеть, что между строками 273 и 278 мы объявляем некоторые переменные. Переменные, объявленные как статические, - это переменные, которые могут располагаться в глобальной области видимости класса. Однако, поскольку они используются только в данной процедуре и нам нужно сохранить их значение между вызовами, мы определяем их как статические. Если внимательно посмотреть, то увидим, что переменная, объявленная в строке 278, не является статической. Но она инициализируется в момент её объявления. Это позволяет избежать проблем, связанных с неправильным использованием данной переменной. Обратите внимание, что присвоенное значение соответствует событию, объявленному в файле Defines.mqh.

Давайте начнем с анализа первого управляемого события, CHARTEVENT_CHART_CHANGE, которое начинается в строке 282 и заканчивается в строке 290. Данное событие генерируется каждый раз, когда график претерпевает изменения. Процедура не является критической, поскольку состоит только из вычислений, направленных на позиционирование и удерживание объекта OBJ_CHART в соответствующем месте графического окна.

Следующее событие - CHARTEVENT_MOUSE_MOVE (которое начинается в строке 291 и продолжается до строки 338) и является самым длинным событием в процедуре DispatchMessage. Данное событие запускается при перемещении мыши или нажатии ее кнопок. Однако по умолчанию MetaTrader 5 не запускает это событие. Нам необходимо явно указать, что мы хотим получать события мыши. Эту задачу, то есть запрос у MetaTrader 5 на срабатывание этих событий, выполняет индикатор мыши. Чтобы понять, как работает данный индикатор, давайте ознакомимся с предыдущими статьями этой серии. Очень важно подробно разобраться в работе индикатора мыши, поскольку именно он будет отвечать за взаимодействие пользователя с элементами, которые мы создаем и реализуем.

Но обратите внимание, что мы не обрабатываем события мыши напрямую. Посмотрите на строку 292, где мы первым делом проверяем, осуществляется ли щелчок левой кнопкой мыши. Однако для того, чтобы данный клик был действительным, мы просим у индикатора мыши подтвердить его действительность. Но для чего мы это делаем? Почему бы нам просто не проверить действителен ли клик или нет прямо в обработчике? Зачем спрашивать у индикатора мыши?

Причиной обращения к индикатору мыши для проверки достоверности нажатия является то, что он также используется для создания исследований. Когда индикатор мыши выполняет исследование по просьбе пользователя, щелчок не считается действительным. Мы не можем точно определить, когда и как долго индикатор мыши будет находиться в режиме исследования. Поэтому самый эффективный и безопасный способ проверить это - спросить у индикатора мыши. Если индикатор подтверждает, что клик действителен, то мы преобразовываем позиции, о которых сообщает MetaTrader 5, и проверяем, на каком объекте был сделан клик. Это делается с помощью вызова CheckMousePosition. Мы можем использовать координаты, предоставляемые индикатором мыши. Однако, если бы мы это сделали, было бы необходимо прочитать буфер индикатора. Поскольку значения будут такими же, как и те, что предоставляет непосредственно MetaTrader 5, нет смысла читать буфер, чтобы получить данную информацию.

Прежде, чем анализировать, как обрабатывается действительный щелчок, давайте рассмотрим, что происходит в строке 329, которая обрабатывает случай, когда событие CHARTEVENT_MOUSE_MOVE не получило действительного щелчка или было вызвано каким-либо движением, совершенным пользователем, взаимодействующим с мышью. В данном случае в строке 331 переменной sz присваивается отрицательное значение. Затем, мы проверяем в строке 332 имеет ли переменная sx положительное значение. Если всё так, значит, что мы обрабатываем событие мыши, но пока мы оставим это. Поэтому в строке 334 мы сообщаем MetaTrader 5, что мышь может перемещать график. Затем, в строке 335, мы присваиваем переменным sx и sy отрицательное значение, что приводит к провалу теста из строки 332 и предотвращает ненужное выполнение строки 334.

Следует отметить, что после события OnTick, которое управляется в советнике, событие CHARTEVENT_MOUSE_MOVE является одним из самых частых событий. В некоторых случаях это событие CHARTEVENT_MOUSE_MOVE намного превосходит событие OnTick. Если не принять необходимые меры, платформа MetaTrader 5 станет очень медленной и перегруженной просто для обработки этих событий. Поэтому в MetaTrader 5 это событие по умолчанию отключено.

Хорошо, мы разобрались с этим. Давайте ещё раз посмотрим, что происходит между строками 293 и 328, поскольку именно в этой части кода мы напрямую взаимодействуем с некоторыми элементами Chart Trade. Когда CheckMousePosition возвращает значение, оно может указывать на один из объектов, упомянутых в данной части кода, например, на строку заголовка или кнопки покупки или продажи по рыночной цене или закрытия позиции. Другие элементы обрабатываются в других местах и будут описаны позже, но каждый из этих четырех элементов требует разного подхода. Начнем с самого сложного: строка заголовка.

Когда щелчок осуществляется в строке заголовка, у пользователя может возникнуть желание переместить Chart Trade. Поэтому между строками 295 и 313 находится вся необходимая для этого логика. Мы не будем сейчас вдаваться в подробности, поскольку данный процесс уже описали в другой статье этой серии, где мы создали первые версии Chart Trade. Если у вас есть вопросы, прочитайте предыдущие статьи, они также важны для того, что мы разрабатываем здесь. Помните, что код откуда-то появляется: он постепенно создавался и внедрялся, пока не приобрел ту форму, которую вы видите сейчас.

Часть, которая нас интересует, находится между строками 314 и 328, поэтому обратите внимание на значение, определенное в строке 278. Это важно, потому что здесь мы будем тестировать два из трех используемых условий. Хотя мы обрабатываем три условия, первое, которое мы проверяем, - был ли действительным клик по кнопке покупки по рыночной цене. Если это верно, значение будет присвоено переменной ev, чтобы выделить событие покупки по рыночной цене. Это делается в строке 315.

Теперь прошу заметить: В строке 316 мы проверяем, произошло ли нажатие на кнопку продажи по рыночной цене. Однако нам также нужно проверить, произошел ли щелчок на кнопке покупки по рыночной цене, чтобы правильно присвоить значение переменной ev. Зачем нам эта проверка, если мы обрабатываем другое событие? Да, это отдельное событие.

Но дело в том, что все три события (покупка по рыночной цене, продажа по рыночной цене и закрытие позиции) выполняют один и тот же тип действий. Между событиями нет паузы, наоборот, - они располагаются в строку (string). Поэтому в строке 317 мы должны проверить, изменило ли предыдущее событие значение переменной ev. Если это верно, то мы не должны выполнять это действие для следующего события в строке. В противном случае срабатывает только последнее событие, игнорируя предыдущие.

В любом случае мы всегда придем к строке 318, где обрабатывается событие закрытия позиции. Только в этом событии будут выполнены тесты, необходимые MetaTrader 5 для запуска пользовательского события. Теперь мы рассмотрим, как добраться до этой точки, чтобы избежать возможных провалов.

В строке 319 мы проверяем, развернут ли Chart Trade или нет. Данный тест важен, поскольку функция CheckMousePosition не учитывает, развернуто ли окно. Обратите внимание: если мы не осуществим эту проверку и щелкнем в том месте, где должна быть одна из кнопок, пользовательское событие сработает, даже если окно не будет развернуто. Чтобы избежать этой проблемы и убедиться, что событие срабатывает только тогда, когда объекты видны на графике, мы проверяем данное условие.

Здесь возникает важный момент: окно должно быть развернуто на графике, то есть кнопки должны быть видны. Однако в процессе тестирования есть небольшая проблема. Именно наш код, Chart Trade, выполняет данную проверку, а не MetaTrader 5. Почему это важно? Проблема возникает, если кнопки закрывает другой объект. Если мы разместим что-то, закрывающее кнопки, а Chart Trade будет развернут, то при нажатии на область, где расположены кнопки, именно наш код, а не MetaTrader 5, вызовет пользовательское событие. Эта ошибка, которую я планирую исправить в будущем. Однако, поскольку это не столь критично, мы можем пока с этим смириться. Но вам следует учесть эту проблему, если вы используете Chart Trade на реальном счете. НЕ РАЗМЕЩАЙТЕ НИЧЕГО ПЕРЕД КНОПКАМИ, КОГДА Chart Trade РАЗВЕРНУТ.

В этой же строке можно найти еще одну проверку, под номером 319, которая призвана проверить и предотвратить срабатывание нескольких событий от правильного щелчка, за которым следует движение мыши. Это простой тест, в котором значение sz должно быть отрицательным. Если оно положительное, это означает, что событие уже сработало, а кнопка мыши всё ещё нажата. Когда кнопка будет отпущена и произойдет движение мыши, строка 331 обеспечит запуск нового пользовательского события. Хотя это простая проверка, она позволяет избежать множество проблем.

Вся "магия" происходит в строке 321. Чтобы она не была слишком длинной, ее разделили на две части. Поэтому строку 322 также следует рассматривать как часть строки 321. Прежде, чем объяснять данную строку, давайте посмотрим, что делают следующие четыре строки. Начнем со строки 323, которая выведет на панель инструментов команду, отправленную через пользовательское событие. Вам может показаться, что в этом нет необходимости, но поверьте, это поможет избежать многих проблем. Если в окне сообщений мы увидим сообщение, подобное тому, которое выведется при отправке пользовательского события, это предупредит нас о необходимости проверить, что могло произойти. Поэтому никогда не игнорируйте сообщения, которые появляются в окне сообщений MetaTrader 5, многие из них очень важны.

В строке 324 выполняется блок, который позволяет избежать условия, описанного выше, поскольку переменная sz используется при проверке в строке 319. Пользовательское событие, с другой стороны, запускается в строке 325. Прошу заметить, что, в отличие от индикатора управления, здесь данные передаются через область строки. Если бы это было невозможно, мы бы столкнулись с проблемами при передаче больших объемов информации между приложениями.

Теперь давайте прокомментируем один момент. Строки в MQL5 работают по тому же принципу, что и строки в C/C++, то есть они заканчиваются символом NULL. Но почему я упоминаю это? Потому что очень важно понять, почему мы должны использовать функцию StringFormat именно здесь. Если вам не нужно печатать то, что отправляется через пользовательское событие, можно просто взять содержимое строки 321 и поместить его в строку 325, заменив переменную szTmp на функцию StringFormat и используя те же значения, что и в строке 321.

Возвращаясь к теме символьных строк, отметим, что проблема заключается в том, что нам нужно передать числовые значения внутри строки. Теперь можно спросить: почему бы не использовать поля lparam и dparam в сообщении? Зачем использовать поле sparam? Причина - в объеме информации, которую необходимо передать. Если бы параметров было немного, мы могли бы использовать поля lparam и dparam. Но поскольку объем информации значительный, мы должны использовать поле sparam. Именно здесь многие совершают ошибки, особенно начинающие программисты.

Когда мы смотрим, к примеру, на число 65, то при одном применении это значение будет представлять число 65, а при другом - букву А. Это может смутить некоторых людей, но проблема в том, что число 65 интерпретируется буквально. Правильнее было бы смотреть на это с точки зрения бинарности. В этом случае оно будет представлено в 8 битах как: 0100 0001. Это правильный способ анализа когда мы имеем дела с программированием. Так что давайте анализировать ситуацию с правильного ракурса. При рассматривании строки, не следует думать о ней как о ряде печатаемых или понятных человеку символов. Мы должны воспринимать ее как огромное число, состоящее не из одного байта или 256 байт, а из очень большого количества байт.

С этой точки зрения легче понять другой вопрос: как указать, что определенный байт соответствует концу строки? Некоторые языки программирования используют значение в начале строки, так называемое "мертвое" значение, которое указывает на количество символов или байт, которые будет содержать строка. Примером может служить язык Basic. Другим примером может служить старый Pascal, в котором длина строки хранится в ее начале.

Таким образом, внутри самой строки, которую в данном случае следует рассматривать как массив, мы можем иметь любое значение в байтах, то есть значения от 0 до 255. Хотя данный метод подходит во многих ситуациях, для других он оказывается неэффективен. А это связано с тем, что использование массива фиксированного размера может привести к неправильному использованию памяти. Например, нам может понадобиться всего 3 байта, но если компилятор обнаружит, что в одной и той же строке (или, другими словами, массиве) иногда требуется 50, а иногда 20 байт, он выделит под массив большую область, то есть 50 байт, даже если иногда требуется всего 3 байта.

Из-за этой проблемы другие языки программирования не используют данную настройку для строк. А как же узнать, когда строка заканчивается? Наиболее распространенным решением является определение кода или значения, указывающего на конец строки. Обычно используется символ NULL, который в двоичном виде представляется как 0000 0000. Выбор данного символа связан с решениями, принятыми при реализации компилятора. Можно было бы выбрать любой символ, но здесь важно не это, а то, как это влияет на передачу сообщений строками.

Вернемся к случаю с числовыми значениями. Наш Chart Trade будет в основном передавать два типа значений: short y double. В типе short используется 16 бит или 2 байта. Не вдаваясь в подробности о типе double, для решения вопроса мы будем использовать только тип short. А что в Chart Trade использует значение short? Уровень кредитного плеча. Какое наименьшее значение может быть у плеча? Наименьшее значение - 1. Как это значение представлено в двоичном виде? Оно представлено следующим образом: 0000 0000 0000 0001. Здесь мы пишем его для отображения в 16-битном формате.

Однако, хотя тип short использует 2 байта, строка на самом деле состоит из 1 байта на символ. Поэтому первый байт значения 1 в строке должен представлять символ NULL. Значит, строка закончится раньше, чем будет считана какая-либо информация? Да, поэтому мы должны преобразовать значения в печатные символы, то есть, каким бы ни было исходное двоичное значение, оно преобразуется в понятную и пригодную для печати форму, передается, а затем снова преобразуется в соответствующее двоичное значение. Для этого необходима функция StringFormat.


Заключительные идеи

Хотя кажется, что данная статья закончилась внезапно, я не чувствую себя комфортно, объясняя за короткое время и пространство вторую часть, которую нам обязательно необходимо охватить: протокол взаимодействия между приложениями. Хотя вам может показаться, что тема протокола проста, на самом деле ее не так просто объяснить вкратце. Я могу просто сказать, что строка 321 создает протокол, который мы будем использовать.

Однако при таком подходе весь вопрос о коммуникации между программами останется совершенно неопределенным и не поможет вам, если вы попытаетесь понять, как использовать эти знания в своих программах. Хотя MetaTrader 5 существует уже довольно давно и многие программисты заинтересованы в разработке инструментов для него, я не заметил, чтобы кто-то использовал то, что я показываю здесь. Большинство людей, которые хотят чего-то добиться, начинают с нуля или создают огромные программы, которые, в большинстве случаев, крайне сложно поддерживать, корректировать или улучшать. Однако благодаря знаниям, которыми мы здесь поделимся, вы сможете разрабатывать небольшие и гораздо более простые программы, которые к тому же гораздо легче корректировать и поддерживать.

Поэтому я хотел бы воспользоваться возможностью Chart Trade, чтобы подробно объяснить, как работает процесс обмена сообщениями. Я надеюсь, что, будучи начинающим программистом, вы поймете, что мы можем гораздо больше, чем обычно принято делать.

Я абсолютно уверен, что если вы приложите немного усилий, то сможете полностью понять каждое из остальных событий(CHARTEVENT_OBJECT_ENDEDIT, CHARTEVENT_OBJECT_CLICK и CHARTEVENT_OBJECT_DELETE). Код, связанный с этими событиями, намного проще, чем тот, о котором мы говорили в большей части данной статьи.

В следующей статье мы подробно объясним, почему строка 321 имеет такой формат и что это означает для кода советника, получающего события Chart Trade. Это позволит советнику исполнять ордера, сгенерированные при взаимодействии пользователя с кнопками Chart Trade.

Перевод с португальского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/pt/articles/12443

Прикрепленные файлы |
Anexo.zip (420.65 KB)
Самооптимизирующийся советник на языках MQL5 и Python (Часть VI): Использование преимуществ глубокого двойного спуска Самооптимизирующийся советник на языках MQL5 и Python (Часть VI): Использование преимуществ глубокого двойного спуска
Традиционное машинное обучение учит специалистов быть бдительными и не допускать переобучения своих моделей. Однако эта идеология подвергается сомнению в связи с новыми открытиями, опубликованными исследователями из Гарварда, которые обнаружили, что то, что кажется переобучением, в некоторых обстоятельствах может быть результатом преждевременного прекращения процедур обучения. Мы покажем, как можно использовать идеи этой научной публикации для улучшения использования ИИ при прогнозировании доходности рынка.
От начального до среднего уровня: Плавающая точка От начального до среднего уровня: Плавающая точка
Эта статья является кратким введением к понятию числа с плавающей точкой. Поскольку этот текст очень сложный, советую вам прочитать его спокойно и внимательно. Не рассчитывайте быстро освоить систему с плавающей точкой, она становится понятной только со временем, по мере появления опыта использования. Но эта статья поможет вам понять, почему ваше приложение иногда выдает результат, отличный от ожидаемого.
Нейросети в трейдинге: Интеллектуальный конвейер прогнозов (Time-MoE) Нейросети в трейдинге: Интеллектуальный конвейер прогнозов (Time-MoE)
Предлагаем познакомиться с современным фреймворком Time-MoE, адаптированным под задачи прогнозирования временных рядов. В статье мы пошагово реализуем ключевые компоненты архитектуры, сопровождая их объяснениями и практическими примерами. Такой подход позволит вам не только понять принципы работы модели, но и применить их в реальных торговых задачах.
Инженерия признаков с Python и MQL5 (Часть I): AI-модели для долгосрочного прогнозирования по скользящим средним Инженерия признаков с Python и MQL5 (Часть I): AI-модели для долгосрочного прогнозирования по скользящим средним
Скользящие средние являются, безусловно, самыми эффективными индикаторами для прогнозирования моделями ИИ. Однако точность результатов можно еще больше повысить, если перед этим соответственным образом преобразовать данные. В этой статье мы поговорим о создании AI-моделей, которые могут прогнозировать в более отдаленное будущее без существенного снижения уровня точности. В очередной раз мы с вами убедимся, насколько полезны скользящие средние.