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

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

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

Введение

В предыдущей статье "Разработка системы репликации (Часть 76): Новый Chart Trade (III)", я объяснил действительно важную часть кода DispatchMessage и начал объяснять, как должен быть создан процесс взаимодействия или протокол связи.

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


Дополнительная стабилизация кода DispatchMessage

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

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))
293.                   {                  
294.                      switch (CheckMousePosition(x = (short)lparam, y = (short)dparam))
295.                      {
296.                         case MSG_MAX_MIN:
297.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
298.                            break;
299.                         case MSG_DAY_TRADE:
300.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
301.                            break;
302.                         case MSG_LEVERAGE_VALUE:
303.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
304.                            break;
305.                         case MSG_TAKE_VALUE:
306.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
307.                            break;
308.                         case MSG_STOP_VALUE:
309.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
310.                            break;
311.                         case MSG_TITLE_IDE:
312.                            if (sx < 0)
313.                            {
314.                               DeleteObjectEdit();
315.                               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
316.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
317.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
318.                            }
319.                            if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
320.                            if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
321.                            if (m_Info.IsMaximized)
322.                            {
323.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
324.                               m_Info.y = (my > 0 ? my : m_Info.y);
325.                            }else
326.                            {
327.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
328.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
329.                            }
330.                            break;
331.                         case MSG_BUY_MARKET:
332.                            ev = evChartTradeBuy;
333.                         case MSG_SELL_MARKET:
334.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
335.                         case MSG_CLOSE_POSITION:
336.                            if ((m_Info.IsMaximized) && (sz < 0))
337.                            {
338.                               string szTmp = StringFormat("%d?%s?%c?%d?%.2f?%.2f", ev, _Symbol, (m_Info.IsDayTrade ? 'D' : 'S'), m_Info.Leverage, 
339.                                                          FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
340.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
341.                               sz = x;
342.                               EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
343.                            }
344.                            break;
345.                      }
346.                      if (sz < 0)
347.                      {
348.                         sz = x;
349.                         AdjustTemplate();
350.                         if (obj == MSG_NULL) DeleteObjectEdit();
351.                      }
352.                   }else
353.                   {
354.                      sz = -1;
355.                      if (sx > 0)
356.                      {
357.                         ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                  
358.                         sx = sy = -1;
359.                      }
360.                   }
361.                   break;
362.                case CHARTEVENT_OBJECT_ENDEDIT:
363.                   switch (obj)
364.                   {
365.                      case MSG_LEVERAGE_VALUE:
366.                      case MSG_TAKE_VALUE:
367.                      case MSG_STOP_VALUE:
368.                         dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
369.                         if (obj == MSG_TAKE_VALUE)
370.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
371.                         else if (obj == MSG_STOP_VALUE)
372.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
373.                         else
374.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
375.                         AdjustTemplate();
376.                         obj = MSG_NULL;
377.                         ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
378.                         break;
379.                   }
380.                   break;
381.                case CHARTEVENT_OBJECT_CLICK:
382.                   if (sparam == m_Info.szObj_Chart) if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) switch (obj = CheckMousePosition(x = (short)lparam, y = (short)dparam))
383.                   {
384.                      case MSG_DAY_TRADE:
385.                         m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
386.                         DeleteObjectEdit();
387.                         break;
388.                      case MSG_MAX_MIN:
389.                         m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
390.                         DeleteObjectEdit();
391.                         break;
392.                      case MSG_LEVERAGE_VALUE:
393.                         CreateObjectEditable(obj, m_Info.Leverage);
394.                         break;
395.                      case MSG_TAKE_VALUE:
396.                         CreateObjectEditable(obj, m_Info.FinanceTake);
397.                         break;
398.                      case MSG_STOP_VALUE:
399.                         CreateObjectEditable(obj, m_Info.FinanceStop);
400.                         break;
401.                   }
402.                   if (obj != MSG_NULL) AdjustTemplate();
403.                   break;
404.                case CHARTEVENT_OBJECT_DELETE:
405.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
406.                   break;
407.             }
408.             ChartRedraw();
409.          }
410. //+------------------------------------------------------------------+

Фрагмент файла C_ChartFloatingRAD.mqh

Прошу заметить, что некоторые строки удалены, например строки 314 и 341, которые были перенесены в тест на строке 346, чтобы исправить проблему, возникавшую при нажатии на некоторые элементы управления. Обратите внимание, что переменная sz используется в каждом из объектов. Это наблюдается на строках 297, 300, 303, 306 и 309, а также в существующих тестах на строках 312 и 336.

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

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


Понимание планирования протоколов сообщений

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

В предыдущей статье мы объяснили, зачем нужно преобразовывать числовые значения в соответствующий литерал, то есть, если нам нужно передать двоичное значение 0001 0011, нам нужно будет преобразовать его в строку, представляющую значение 19, поэтому нам нужно будет передать 2 байта вместо 1. Хотя это может показаться неэффективным, цель состоит не в том, чтобы быть эффективным, а в том, чтобы обеспечить правильную передачу информации, а если это можно сделать эффективно, тем лучше. Но самое главное, чтобы информацию правильно поняли на другом конце.

Как объяснялось в предыдущей статье, мы будем использовать поле sparam. Теперь нам нужно подумать о следующем: если информация будет передаваться по одной строке, как мы сможем понять, когда заканчивается одна часть информации и начинается другая? Ведь это ключевой вопрос.

Нужно придумать или спланировать, как это сделать. Мы должны разместить информацию таким образом, чтобы потом ее можно было извлечь. Есть несколько различных и в то же время взаимодополняющих способов сделать это. Один из них - поместить каждый фрагмент информации в массив фиксированного размера. Данные массивы объединяются в строку, которая передается. У этого подхода есть свои преимущества и недостатки.

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

Чтобы было понятнее, можно посмотреть на рисунок ниже:

Изображение 1

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

Однако, мы можем использовать и другой подход: что, если вместо массива фиксированного размера разместить массив, указывающий на размер ожидаемой информации? Это сделает строку намного лучше и облегчит поиск нужной информации. Но несмотря на то, что такой подход хорош, у него есть проблема. Нам потребуется написать больше кода. По мере того, как мы создаем всё больше кода, нам нужно его тестировать. Кроме того, во многих случаях, если информация превышает установленный в реализации лимит, при сборке строки произойдет ее потеря. В идеальном случае, как показано на изображении ниже:

Изображение 2

Как видно на первом изображении, синий блок указывает, где находится символ, который обозначает конец строки. Обратите внимание, что здесь у нас идеальное состояние, когда каждый цвет представляет информацию, которая будет сжата в строке. Хотя в некоторых случаях это может усложнить распаковку строки, для полноценного и корректного восстановления исходной информации этот случай представляется вполне адекватным. Однако в этом случае у каждого набора будет фиксированный размер. Тогда мы получим более совершенный подход, чем тот, который показан на первом рисунке. Но у нас есть проблема. А именно: что, если информация, которая должна быть в зеленом наборе, вместо двух байт потребует три байта?

Это был бы наихудший сценарий. И это связано с тем, что один из байтов будет потерян во время сборки зеленого набора. Но можно подумать и так: а что, если бы зеленый набор мог увеличиться, чтобы вместить этот дополнительный байт? Можно ли такое сделать? Нет. Если мы это сделаем, вся информация после зеленого набора будет скомпрометирована, и получатель не сможет понять, что в зеленом наборе три, а не два байта, как он ожидал.

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

А что, если вместо блока фиксированного размера создать блок переменного размера, в котором информация может быть любого размера? Это было бы гораздо лучше, так ведь? В каком-то смысле да, но у нас есть проблема: как сообщить получателю сообщения, когда заканчивается один набор и начинается другой? Теперь нам предстоит решить еще одну проблему. И здесь вам нужно внимательно подумать, потому что если мы не будем достаточно спокойно обдумывать свои действия, мы отправим сообщение, которое не будет понято получателем, даже если оно правильно именно для вас. Но как такое возможно, что для получателя всё не так однозначно?

Подумайте вот о чем: каждый раздел сообщения может быть любого размера, что позволяет нам передавать практически любой тип информации. Информация должна располагаться в определенной последовательности, какой бы она ни была, но последовательность должна соблюдаться всегда. Пока всё хорошо. У нас нет проблем, но за исключением этой: как указать на то, что один раздел закончен, а другой начинается?

Как уже говорилось ранее, это самая сложная часть. Всё зависит от типа данных, которые мы помещаем в строку. Если мы используем все 255 возможных значений в байте - опять же, мы должны избегать нулевого символа, поэтому 255, а не 256 - у нас возникает большая проблема: как указать, что мы предоставляем другую информацию в блоке строк. Однако, если мы сократим значения до символов между значениями 32 и 127, нам придется еще поработать над сборкой. Но это позволяет нам использовать любое значение между 128 и 255 в качестве маркировочного символа.

Однако мы можем ограничить всё ещё больше. Для передачи нужной информации мы можем использовать только буквенно-цифровые символы. Это открывает возможность использования знаков препинания или разделительных символов, чтобы указать, когда начинается или заканчивается часть информации во всей строке. И почему мы можем сделать это здесь? Причина в том, что данные, которые нам нужно передать, в основном представляют собой информацию о названии символа и некоторые числовые значения, такие как уровень плеча, значения тейк-профита и стоп-лосса. Они настраиваются в Chart Trade, но должны быть переданы в советник (EA).

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

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

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


Объединяем лучшее из каждого мира

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

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

Изображение 3

На этом изображении, чуть выше, можно увидеть реальный пример отправки Chart Trade. Но что же несет в себе это безумное сообщение? Чтобы понять это, мы должны рассмотреть, что происходит в строке 338 фрагмента. Хотя это сообщение может показаться запутанным, оно следует протоколу отправки. По сути, мы используем лучшее из двух миров. Оно позволяет блокам в строке иметь любой размер и в то же время определенным образом индексирует информацию в строке.

Можно не понимать, как происходит индексация, и на самом деле это не очень очевидно, но она существует. Обратите внимание на символ D в сообщении. Прошу заметить, что до и после него идет один и тот же символ и что в этот момент только один блок заполняется символом D. В других местах сообщения дело обстоит иначе. Это указывает на то, что данный символ может быть проиндексирован каким-либо образом. Но это станет ясно позже, а пока давайте разберемся с происходящим.

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

Один важный момент: данное значение 9 соответствует событию покупки на рынке. Однако оно может быть и другим; например, если бы у нас в этом блоке было значение 11, это означало бы закрытие всех позиций. В данном случае в блоке будет два символа, а не один, как показано в этом примере. Но почему для покупок значение равно 9, а для закрытия позиций - 11? Откуда берется это значение? Это очень правильный вопрос. Прошу заметить, что в строке 338 первым значением, помещенным в строку, является значение ushort. Однако, это не объясняет, почему 9 означает "купить", а 11 - "закрыть", не так ли? На самом деле, оно не объясняет это.

Но посмотрите на строку 278. Откуда берется это значение? Данное значение берется из заголовочного файла Defines.mqh. А теперь прошу вас быть внимательными. В файле Defines.mqh есть перечисление: EnumEvents. Такое перечисление начинается с нуля и увеличивается на одно значение с каждым новым элементом. Затем, считая от первого события (evHideMouse), мы имеем девятое событие (evChartTradeBuy) и десятое (evChartTradeCloseAll). Теперь вы знаете, откуда берутся значения, которые вы видите в начале строки. Они берутся от перечисления EnumEvents.

Давайте двигаться дальше. Обратите внимание, что все знаки вопроса выделены фиолетовым цветом. Символ NULL, который закрывает строку, выделен синим цветом. Поскольку каждый блок разграничен символом вопросительного знака, можно разместить столько буквенно-цифровых символов, сколько необходимо для отправки сообщения. Но надо учитывать, что это сообщение должно быть построено в определенной последовательности. Помните: получатель ожидает данные в определенном порядке. Хотя мы можем расположить информацию в случайном порядке, получатель этого не ожидает, по крайней мере в том варианте, который мы собираемся показать.

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

Кроме того, позже мы увидим, что эта информация о сообщении может быть использована и для других целей. В этом примере символом является BOVA11, который представляет собой ETF. Данная часть имени символа - то, что могло бы сильно усложнить ситуацию, если бы мы не использовали разделитель. Это связано с тем, что на некоторых рынках название символа состоит из четырех буквенно-цифровых символов, в то время как в других случаях их может быть пять. В данном случае у нас пять символов. Однако даже на B3 (Бразильская фондовая биржа) есть несколько символов, использующих четыре символа (знака).

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

Теперь вернемся к нашему символу D. В этот момент мы хотим обратить внимание на строку 338. Обратите внимание, что если совершаемая операция не относится к тому типу, который закрывается в тот же день (т.е. если это не дневная сделка), то символ, который будет находиться в данном блоке, на самом деле будет другим и будет заменен на S. Мы можем использовать любой другой символ, но нельзя забыть изменить его и у получателя, иначе связь будет более сложной, поскольку получатель может не понять, что обозначает буква или набор символов.

Сразу после этого у нас есть буквальное значение, в данном случае 250. Что представляет собой данное значение? Опять же, обратимся к коду в строке 338, чтобы понять, что это значение - желаемый уровень плеча. Здесь кроется интересный вопрос. В данном случае мы используем три численных символа для обозначения значения плеча, которое мы хотим использовать.

Но не лучше ли использовать для этого двоичное значение? На самом деле, это кажется вполне уместным, так как невозможно использовать нулевое плечо. Однако существует условие, которое препятствует этому. Я не имею в виду то условие, при котором мы не можем отформатировать строку с символом, соответствующим значению 250, или любое другое. Я говорю о проблеме символа или знака, используемого в качестве разделителя. Обратимся к таблице ASCII и проверим, какое значение имеет знак вопроса. Мы увидим, что значение равно 63.

Чтобы облегчить вам жизнь, на рисунке ниже представлена таблица ASCII, начиная с символа 0 и заканчивая символом 128.

Изображение 4

Почему данное значение важно для нас? Причина в том, что любое используемое нами значение плеча, включая 63, будет интерпретировано получателем, который я вскоре покажу, как символ-разделитель. То есть получатель не сможет понять, что четвертый блок относится к значению, которое будет использоваться в качестве уровня плеча.

Но потом можно подумать: а что, если я изменю это условие, перейдя к другому значению? Например, если добавить к этому уровню плеча значение 63, зная, что значение плеча никогда не будет равно нулю. Итак, первое значение, которое построим, будет 64. Проблема решена? Хотелось бы, чтобы всё было так просто. Но нет; добавив 63 к значению плеча, - мы, по сути, лишь отложим решение проблемы на другой раз.

Теперь всё стало еще сложнее. Потому что я не понимаю это. Как такое возможно? Если добавить 63 к значению плеча, все значения всегда будут больше 63. Да, на самом деле, все значения будут больше 63, и в этом весь смысл. В программировании ни одно значение, по сути, не является бесконечным, каждое значение ограничено числом. Максимальное количество зависит от размера используемого слова. Даже на 64-битном процессоре, на котором сейчас работает MetaTrader 5, система управления основана на 8-битных концепциях.

То есть, даже используя 64-битный процессор, мы не сможем посчитать в символах значение 2, возведенное в степень 64, что было бы: 18.446.744.073.709.551.615. В реальности мы могли считать только до 255, что эквивалентно числу от 2 до 8. Но почему? Может быть не существует способа решения этой проблемы? Да, есть пути решения. Один из них - использовать таблицу, которая отличается от ASCII. В качестве примера можно привести использование таблиц Unicode.

Однако есть и другая проблема. Вызов StringFormat не использует таблицу Unicode, по крайней мере, на момент написания статьи. Строковые функции MQL5 в основном следуют принципам C/C++, то есть используют таблицу ASCII. Хотя в C/C++ есть функции для работы с таблицей Unicode, изначально это было не так.

Поэтому, даже если мы добавим 63 к значению плеча, составное значение будет генерироваться каждые 255 позиций. Данное значение представляет собой комбинацию коэффициента и значения 63. Коэффициент показывает, сколько раз произошел цикл счета до 255. Чтобы вы поняли, значение 575 - это комбинация 2 - множителя, плюс 63 - значение текущего отсчета. И так далее.

Таким образом, для представления вещей нам понадобится два байта, где второй байт всегда будет значением 63 в определенный момент времени. Первый байт будет значением коэффициента, то есть сколько раз счетчик достиг максимально допустимого предела, который всегда будет равен 2, увеличенным на 8, как объяснялось выше. Подобные вещи имеют различные математические следствия, но мы не объясним их здесь и сейчас, поскольку они связаны с вещами, которые выходят за рамки текущей статьи.

Чтобы закончить объяснение создания протокола сообщений, заметим, что у нас есть два значения, которые могут быть представлены в виде double или float. Однако по тем же причинам, что и в случае с уровнем плеча, мы должны использовать эти значения буквально. Именно поэтому они выглядят так, как можно видеть на картинке.

Но теперь возникает вопрос, который вы наверняка себе задаете: почему данные значения выглядят именно так? Что они на самом деле представляют? Эти значения могут показаться странными, потому что мы можем забыть, что находится в строке 338 фрагмента кода. Прошу заметить, что здесь мы преобразуем финансовое значение в значение в пунктах. Таким образом, значение 3,60 представляет собой финансовое значение в $900, а значение 3,02 - в $755.

Так почему бы не использовать финансовые значения вместо пунктов? Причина - в простоте. Гораздо проще реализовать советник, который использует уже преобразованные значения, чем преобразовывать данные значения внутри советника. Возможно, сейчас вам это не совсем понятно, но вы увидите, что мы можем использовать это по-разному. Однако, это мы оставим на будущее, поскольку есть вещи, которые необходимо объяснить, чтобы действительно понять всю глубину послания, уже заложенного в советнике. Но, как я уже сказал, это на будущее.


Заключение

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

Всё это должно быть спланировано сейчас, потому что если мы сделаем это позже, то запутаемся в попытках заставить протокол связи передавать информацию. Данная информация будет необходима советнику, чтобы знать, что и как делать. Так что не откладывайте ее изучение. Начните сейчас и вносите настройки, которые сочтете нужными. В следующей статье мы рассмотрим вопросы, связанные с получателем, то есть советником.

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

Прикрепленные файлы |
Anexo.zip (420.65 KB)
От начального до среднего уровня: Определения (II) От начального до среднего уровня: Определения (II)
В этой статье мы продолжим знакомство с директивой #define, но на этот раз мы сосредоточимся на второй форме ее использования, то есть на создании макросов. Поскольку данная тема может быть немного сложной, мы решили использовать приложение, которое мы изучаем уже некоторое время. Надеюсь, вам понравится сегодняшняя статья.
Упрощаем торговлю на новостях (Часть 5): Совершаем сделки (II) Упрощаем торговлю на новостях (Часть 5): Совершаем сделки (II)
В этой статье мы детально рассмотрим класс управления сделками, включив в него ордера buy stop и sell stop для торговли новостными событиями, а также введем ограничение срока действия этих ордеров, чтобы предотвратить переносы торговли на следующий день. В советник будет встроена функция проскальзывания, которая попытается предотвратить или минимизировать возможное проскальзывание, которое может возникнуть при использовании стоп-ордеров в торговле, особенно во время выхода новостей.
Загрузка данных Международного валютного фонда на Python Загрузка данных Международного валютного фонда на Python
Загрузка данных Международного валютного фонда на Python: добываем данные IMF для применения в макроэкономических валютных стратегиях. Как макроэкономика может помочь трейдеру и алготрейдеру?
Клиент в Connexus (Часть 7): Добавление клиентского уровня Клиент в Connexus (Часть 7): Добавление клиентского уровня
В настоящей статье мы продолжаем разработку библиотеки Connexus. В настоящей главе мы создаем класс CHttpClient, отвечающий за отправку запроса и получение ордера. Мы также рассматриваем концепцию моков (mocks), отделяя библиотеку от функции WebRequest, что обеспечивает большую гибкость для пользователей.