English Español Português
preview
Разработка системы репликации (Часть 33): Система ордеров (II)

Разработка системы репликации (Часть 33): Система ордеров (II)

MetaTrader 5Примеры | 18 апреля 2024, 15:59
273 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 32): Система ордеров (I) мы начали разрабатывать систему ордеров для использования в советнике. Тогда мы разрабатывали базовый класс системы, содержащий только те функции, которые нам действительно нужны с начала.

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

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

Но прежде чем продолжить рассказ о системе ордеров, я хотел бы показать, как решить одну проблему. В MetaTrader 5 это даже не проблема, а скорее недостаток.


Попытаемся упростить ситуацию

Если вы являетесь постоянным пользователем MetaTrader 5, то наверняка заметили один не очень удобный момент. Особенно его могут заметить те, кто перешел с других платформ на MetaTrader 5. Этот непривычный момент связан с техническим анализом графиков.

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

Если для объектоа не установлен флаг (флажок, см. рис. 01) "Отключить выделение", мы можем получить доступ к объекту двойным кликом.

Рисунок 01

Рисунок 01 - Флажок установлен.

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

Теперь давайте поговорим о деталях. В MetaTrader 5 есть возможность изменить данное поведение. Чтобы перейти к нему, нужно нажать сочетание CTRL + O. Откроется окно опций MetaTrader 5. В этом окне перейдем на вкладку Графики и установим флажок: "Выделение объектов щелчком мыши". Если сделать это, то сможем использовать MetaTrader 5 так же, как и любую другую платформу. Но даже в данном случае нам всё равно придется сделать эту небольшую поправку, и даже тогда у нас всё равно не будет некоторых возможностей. В этом и состоит цель: показать, как сделать это другим способом, но с новыми возможностями.

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

Однако, чтобы решить данную проблему, мы вернемся к классу C_Terminal и добавим в него несколько строк кода. Этого будет достаточно, чтобы обеспечить более легкий опыт для новых пользователей. Далее рассмотрим, что следует добавить в код. Это можно увидеть в коде ниже:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      static string st_str = "";
                                
      switch (id)
      {
         case CHARTEVENT_CHART_CHANGE:
            m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
            m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
            break;
         case CHARTEVENT_OBJECT_CLICK:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
            ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
            break;
         case CHARTEVENT_OBJECT_CREATE:
            if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
            st_str = sparam;
            break;
      }
   }

Данная процедура уже является частью оригинального кода класса C_Terminal. Но здесь добавились несколько дополнительных строк, в которых мы рассмотрим два новых события. Об этом нам сообщит платформа MetaTrader 5. В данном коде есть одна деталь, но я могу объяснить код, а затем рассказать о деталях. Обратите внимание, что мы объявляем статическую локальную переменную, она используется для хранения имени объекта, который мы изучаем. Данная переменная инициализируется как пустая строка. Это очень важно, если мы хотим, чтобы всё было сделано безупречно. Как только мы щелкаем по объекту на графике, его имя передается нашей программе. А также событие, которое в данном случае сообщается платформой как CHARTEVENT_OBJECT_CLICK, и имя объекта будут находиться в переменной sparam. Эти мелкие детали помогут нам на следующем этапе. Теперь давайте проверим, совпадает ли имя в нашей локальной переменной с тем, которое сообщает MetaTrader 5. В случае первого исполнения они будут другими; если они одинаковые, то ничего не произойдет. Но если они разные, то объект, находившийся в фокусе платформы, потеряет его, что и делается в этой строке, где мы проверяем и, при необходимости, снимаем фокус с объекта.

Теперь я хочу, чтобы вы обратили внимание на следующий момент: Когда у объекта изменяются свойства, чтобы он выглядел так, как показано на рисунке 01, мы должны понимать, что пользователь сознательно говорит, что этому объекту не надо уделять внимание незаметно. Либо потому что объект не должен быть изменен, либо потому что он будет на что-то указывать, и мы не хотим менять его положение или изменять что-то подобное. Однако, установив флажок (как показано на рисунке 01), мы должны понимать, что объект будет игнорироваться. Именно это и делает данная проверка. Она проверяет, стоит ли игнорировать данный объект. Если бы эта проверка не была выполнена, флажок, который пользователь установил бы на свойстве объекта (как показано на рисунке 01), был бы проигнорирован, и объект был бы доступен, получив фокус. Таким образом, чтобы заставить объект получить фокус, нужно использовать именно данный код. Обратите внимание, что в этом коде мы храним имя объекта, чтобы при потере фокуса наша программа знала, что это был за объект. Нечто подобное происходит, когда MetaTrader 5 запускает событие CHARTEVENT_OBJECT_CREATE. Однако, здесь есть одна деталь: В отличие от события CHARTEVENT_OBJECT_CLICK, данное событие создания объекта будет происходить в нашей программе только в том случае, если мы сообщим MetaTrader 5, что хотим знать о создании объекта.

Примечание: В любом случае, в MetaTrader 5 всегда будут происходить события, но некоторые из них будут направлены на наш код, только если мы сообщим об этом платформе.

Чтобы сообщить MetaTrader 5, что мы хотим получать уведомления о создании объекта на графике, мы поступаем примерно таким же образом, как и в случае с удалением объекта. Поэтому необходимо добавить следующую строку кода:

C_Terminal()
   {
      m_Infos.ID = ChartID();
      CurrentSymbol();
      m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
      m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
      m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
      m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
      m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
      m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
      m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
      m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
      m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
      m_Infos.ChartMode     = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
      ResetLastError();
   }

Данная строка сообщает MetaTrader 5, что отныне мы хотим получать уведомления о создании нового объекта на графике. Аналогичным образом нам нужно сообщить MetaTrader 5, когда мы больше не хотим получать уведомления, что собственно делается с помощью следующего кода:

~C_Terminal()
   {
      ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
      ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, 0, false);
      ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
   }

Как только данная строка будет выполнена, MetaTrader 5 больше не будет уведомлять наш код о событиях создания объектов. Подобные вещи довольно важны и интересны, поскольку нам иногда хочется узнать, когда пользователь добавляет или удаляет объекты с графика. А пытаться сделать это "вручную" довольно сложно. Однако, с помощью MetaTrader 5 это сделать довольно просто. Думаю, вы наверно заметили, что мы можем подчинить себе платформу, чтобы заставить ее действовать так, как мы ожидаем. Такое поведение и тот факт, что мы можем это делать, означает, что мы можем помочь новым пользователям получить лучший опыт использования платформы, что весьма мотивирует. Всё это вопрос адаптации, но адаптация пройдет быстрее, если вы, как программист, сможете помочь «не программисту».

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


Расширение системы ордеров

Название данной темы, возможно, немного преувеличено, но я хочу привнести в нашу систему ордеров то, что было показано в другой серии статей. Таким образом, мы сможем использовать те же понятия и знания, которые получили там. В этой серии вышла последняя на данный момент статья, с которой можно ознакомиться по данной ссылке: Как построить советник, работающий автоматически (Часть 15): Автоматизация (VII). Там мы проверили, как преобразовать советник, который был построен и управлялся вручную, в автоматический советник. То, что я поднимаю эту тему сейчас, связано с тем, что вам может быть интересно понаблюдать за работой советника на репликации/моделировании, чтобы иметь возможность внести в него какие-то коррективы. Помните, что для тестирования и проверки работоспособности советника в платформе MetaTrader 5 есть тестер стратегий, который отлично справляется со своей задачей. Здесь мы не создаем что-то параллельно с тестером. Идея заключается в том, чтобы была возможность отработать какую-то технику, даже когда рынок закрыт.

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

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


Контроль времени с помощью C_ControlOfTime

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

Класс C_ControlOfTime начинается следующим образом:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Orders.mqh"
//+------------------------------------------------------------------+
class C_ControlOfTime : protected C_Orders
{
   private :
      struct st_00
      {
         datetime Init,
                  End;
      }m_InfoCtrl[SATURDAY + 1];
//+------------------------------------------------------------------+

Здесь мы собираемся хранить в массиве допустимое расписание операций, день за днем. Пожалуй, самой интересной частью данного кода является использование определения SATURDAY. Для тех, кто не читал серию статей об автоматических советниках, я вкратце объясню, что значит использование данного определения. Используя его, мы стремимся сказать, что наша матрица будет охватывать все дни недели. Но в этой истории есть одна важная деталь: нам нужно добавить одну единицу к значению. Это необходимо для того, чтобы у матрицы была правильная длина, поскольку она начинается с НУЛЯ. Однако у нас не можем быть матрица с нулевыми элементами. Мы всегда должны начинать со значения, которое больше нуля. Вот почему мы добавили одну единицу. Если бы мы этого не сделали, мы бы получили матрицу из 6 элементов, а не из 7, как следовало бы ожидать. Еще один момент: вы, наверное, заметили, что класс C_ControlOfTime наследует от класса C_Orders. Таким образом, мы расширим возможности класса C_Orders. Но на самом деле данное расширение не будет перенесено на советник, как вы увидите в последующих объяснениях. Причину этого можно лучше понять из серии статей об автоматическом советнике.

Следующее, что можно увидеть дальше в коде, - это конструктор класса. На это можно посмотреть чуть ниже:

C_ControlOfTime(C_Terminal *arg, const ulong magic)
                :C_Orders(arg, magic)
   {
      for (ENUM_DAY_OF_WEEK c0 = SUNDAY; c0 <= SATURDAY; c0++) ZeroMemory(m_InfoCtrl[c0]);
   }

Здесь нужно понять две простые вещи. Первая - это то, что мы инициализируем класс C_Orders еще до того, как будет выполнен код в конструкторе C_ControlOfTime. Вторая и, возможно, любопытная особенность - наличие цикла. Но самое интересное - это собственно не сам цикл, а то, как он работает. Обратите внимание, что в этом цикле мы используем перечисление, и что оно начинается с SUNDAY, а переменная будет увеличиваться вплоть до SATURDAY. Возможно ли это? Да, мы можем это сделать, но мы должны соблюдать осторожность. В принципе, главная забота заключается в том, чтобы SUNDAY имел меньшее значение, чем SATURDAY. Если это не так, то у нас возникнут проблемы при выполнении доступа к данным матрицы. К счастью, перечисление начинается с SUNDAY и продвигается день за днем до SATURDAY, таким образом, мы получаем дни недели.

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

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

virtual void SetInfoCtrl(const ENUM_DAY_OF_WEEK index, const string szArg) final
   {
      string szRes[], sz1[];
      bool bLocal;
                                
      if (_LastError != ERR_SUCCESS) return;
      if ((index > SATURDAY) || (index < SUNDAY)) return;
      if (bLocal = (StringSplit(szArg, '-', szRes) == 2))
      {
         m_InfoCtrl[index].Init = (StringToTime(szRes[0]) % 86400);
         m_InfoCtrl[index].End = (StringToTime(szRes[1]) % 86400);
         bLocal = (m_InfoCtrl[index].Init <= m_InfoCtrl[index].End);
         for (char c0 = 0; (c0 <= 1) && (bLocal); c0++)
            if (bLocal = (StringSplit(szRes[0], ':', sz1) == 2))
               bLocal = (StringToInteger(sz1[0]) <= 23) && (StringToInteger(sz1[1]) <= 59);
         if (_LastError == ERR_WRONG_STRING_DATE) ResetLastError();
      }
      if ((_LastError != ERR_SUCCESS) || (!bLocal))
      {
         Print("Error in the declaration of the time of day: ", EnumToString(index));
         ExpertRemove();
      }
   }

Данная процедура была описана и разобрана в статье Как построить советник, работающий автоматически (Часть 10): Автоматизация (II), где подробно объяснили, как это работает. Если вам нужна более подробная информация об этом или если вы не понимаете код, прочитайте эту статью, она будет очень полезной. Следующая процедура (показанная ниже) была описана в той же статье. На самом деле, всю проделанную здесь работу над классом C_ControlOfTime можно увидеть в упомянутой выше статье. Разница была только в конструкторе. Но это связано с тем, что мы собираемся делать дальше.

virtual const bool CtrlTimeIsPassed(void) final
   {
      datetime dt;
      MqlDateTime mdt;
                                
      TimeCurrent(mdt);
      dt = (mdt.hour * 3600) + (mdt.min * 60);
      return ((m_InfoCtrl[mdt.day_of_week].Init <= dt) && (m_InfoCtrl[mdt.day_of_week].End >= dt));
   }

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


C_Manager: Класс "администратор".

C_Manager, на самом деле, довольно интересный класс, поскольку он изолирует от самого советника большую часть сложностей, связанных с кодом советника. То есть в советнике у нас будет всего несколько строк кода, а управлять и манипулировать всем будут классы. Отличается от кода, который можно увидеть в серии автоматических советников. Мы начнем с чего-то более простого, но не менее интересного. Главная проблема заключается в том, что советник на самом деле не будет работать во всех ситуациях. Вначале мы сосредоточимся на том, чтобы сделать работу советника максимально безопасной. Поскольку потом нам нужно будет сделать так, чтобы он мог работать и в системе репликации/моделирования. И это самая сложная часть системы.

Чтобы не доводить дело до крайностей, мы сначала заставим советника работать с ресурсами платформы MetaTrader 5. А это значит, что система ордеров на самом деле не такая уж и всеобъемлющая. Её нельзя использовать, например, в режиме CrossOrder, то есть на данном раннем этапе мы не можем использовать систему ордеров для отправки ордеров по активу, отличному от того, с которым работает советник. Это означает, что мы будем использовать систему кросс-ордеров.

Давайте теперь посмотрим на код этого класса C_Manager в текущем состоянии разработки. Код начинается со следующих строк:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_ControlOfTime.mqh"
#include "..\System EA\Auxiliar\Study\C_Study.mqh"
//+------------------------------------------------------------------+
#define def_Prefix "Manager"
#define def_LINE_PRICE  def_Prefix + "_PRICE"
#define def_LINE_TAKE   def_Prefix + "_TAKE"
#define def_LINE_STOP   def_Prefix + "_STOP"
//+------------------------------------------------------------------+
#define def_AcessTerminal       (*Terminal)
#define def_InfoTerminal        def_AcessTerminal.GetInfoTerminal()
#define def_AcessMouse          (*Study)
#define def_InfoMouse           def_AcessMouse.GetInfoMouse()
//+------------------------------------------------------------------+

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

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

class C_Manager : public C_ControlOfTime
{
   private :
      struct st00
      {
         double  FinanceStop,
                 FinanceTake;
         uint    Leverage;
         bool    IsDayTrade,
                 AccountHedging;                                         
      }m_Infos;
      struct st01
      {
         color   corPrice,
                 corTake,
                 corStop;
         bool    bCreate;
      }m_Objects;             
//+------------------------------------------------------------------+
      C_Terminal *Terminal;
      C_Study    *Study;

Здесь мы объявляем частные глобальные переменные класса. Для лучшей организации я использовал и разделял элементы по структурам. Таким образом, впоследствии будет проще изменять и удалять части кода. Это обязательно будет реализовываться в следующих статьях. Сейчас мы попробуем заставить систему работать самым простым способом и максимально использовать возможности MetaTrader 5. Я повторяю это, потому что мы не должны воспринимать данную систему как окончательную. Она всё еще находится в стадии разработки.

Давайте рассмотрим первую процедуру, присутствующую в классе C_Manager. Это видно из следующего кода:

bool CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::CreateOrder(type, Price, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

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

Следующая функция, которая также является эксклюзивной для класса, показана ниже:

bool ToMarket(const ENUM_ORDER_TYPE type)
   {
      ulong tmp;
                                
      if (!CtrlTimeIsPassed()) return false;
      tmp = C_Orders::ToMarket(type, m_Infos.FinanceStop, m_Infos.FinanceTake, m_Infos.Leverage, m_Infos.IsDayTrade);
                                
      return tmp > 0;
   }

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

Чтобы инициализировать такие переменные, в нашем распоряжении есть некоторые ресурсы. На данный момент мы будем использовать самый простой метод - конструктор. Использование конструктора для инициализации переменных класса - это, без сомнения, лучший способ из всех доступных. Однако мы изменим это позже ради практичности. Пока что это будет выполнять то, что нам нужно. Код конструктора можно увидеть ниже:

C_Manager(C_Terminal *arg1, C_Study *arg2, color cPrice, color cStop, color cTake, const ulong magic, const double FinanceStop, const double FinanceTake, uint Leverage, bool IsDayTrade)
          :C_ControlOfTime(arg1, magic)
   {
      string szInfo = "HEDGING";
                                
      Terminal = arg1;
      Study = arg2;
      if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (CheckPointer(Study) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
      if (_LastError != ERR_SUCCESS) return;
      m_Infos.FinanceStop     = FinanceStop;
      m_Infos.FinanceTake     = FinanceTake;
      m_Infos.Leverage        = Leverage;
      m_Infos.IsDayTrade      = IsDayTrade;
      m_Infos.AccountHedging  = false;
      m_Objects.corPrice      = cPrice;
      m_Objects.corStop       = cStop;
      m_Objects.corTake       = cTake;
      m_Objects.bCreate       = false;
      switch ((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE))
      {
         case ACCOUNT_MARGIN_MODE_RETAIL_HEDGING: m_Infos.AccountHedging = true; break;
         case ACCOUNT_MARGIN_MODE_RETAIL_NETTING: szInfo = "NETTING";            break;
         case ACCOUNT_MARGIN_MODE_EXCHANGE      : szInfo = "EXCHANGE";           break;
      }
      Print("Detected Account ", szInfo);
   }

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

У нас также есть деструктор; его можно увидеть ниже:

~C_Manager()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_Prefix);
   }

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


Заключение

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

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


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

Прикрепленные файлы |
Anexo.zip (130.63 KB)
Разрабатываем мультивалютный советник (Часть 8): Проводим нагрузочное тестирование и обрабатываем новый бар Разрабатываем мультивалютный советник (Часть 8): Проводим нагрузочное тестирование и обрабатываем новый бар
По мере продвижения мы использовали в одном советнике всё больше и больше одновременно работающих экземпляров торговых стратегий. Попробуем выяснить до какого количества экземпляров мы можем дойти прежде, чем столкнёмся ограничениями ресурсов.
Алгоритм оптимизации на основе мозгового штурма — Brain Storm Optimization (Часть I): Кластеризация Алгоритм оптимизации на основе мозгового штурма — Brain Storm Optimization (Часть I): Кластеризация
В данной статье мы рассмотрим инновационный метод оптимизации, названный BSO (Brain Storm Optimization), который вдохновлен природным явлением - "мозговым штурмом". Мы также обсудим новый подход к решению многомодальных задач оптимизации, который использует метод BSO и позволяет находить несколько оптимальных решений без необходимости заранее определять количество подпопуляций. В статье мы также рассмотрим методы кластеризации K-Means и K-Means++.
Алгоритм оптимизации на основе мозгового штурма — Brain Storm Optimization (Часть II): Многомодальность Алгоритм оптимизации на основе мозгового штурма — Brain Storm Optimization (Часть II): Многомодальность
Во второй части статьи перейдем к практической реализации алгоритма BSO, проведем тесты на тестовых функциях и сравним эффективность BSO с другими методами оптимизации.
Шаблоны проектирования в программировании на MQL5 (Часть 4): Поведенческие шаблоны 2 Шаблоны проектирования в программировании на MQL5 (Часть 4): Поведенческие шаблоны 2
Статья завершает серию о шаблонах проектирования в области программного обеспечения. Я уже упоминал, что существуют три типа шаблонов проектирования - порождающие, структурные и поведенческие. Мы доработаем оставшиеся паттерны поведенческого типа, которые помогут задать способ взаимодействия между объектами таким образом, чтобы сделать наш код чистым.