English 中文 Español Deutsch 日本語 Português
preview
Разработка системы репликации (Часть 30): Проект советника — класс C_Mouse (IV)

Разработка системы репликации (Часть 30): Проект советника — класс C_Mouse (IV)

MetaTrader 5Тестер | 5 марта 2024, 08:56
439 1
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Разработка системы репликации (Часть 29): Проект Expert Advisor — класс C_Mouse (III) мы разработали класс C_Mouse таким образом, чтобы можно было расширить функциональность исследований, не нарушая ни в коем случае ни одной части нашего кода. Поскольку мы опираемся на технику программирования, позволяющую создавать код параллельно, у нас будет один основной проект, который будет развиваться организованно. В то же время, при желании, в систему можно добавить дополнительные функциональные возможности. Таким образом, при реализации этих функций наш основной код нисколько не изменится и не зависнет при излишнем использовании наследования.

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

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

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

Чтобы продемонстрировать это, мы создадим модификацию класса C_Mouse, но без использования наследования и полиморфизма. В то же время мы получим совершенно иную модель исследования, отличающаяся от оригинальной системы, которая присутствует в классе C_Mouse. Для этого мы создадим новый класс, который может наследоваться (или нет) от класса C_Studies, который мы рассматривали в предыдущей статье. Наследовать или нет класс C_Studys - вопрос скорее личный, чем практический. На самом деле, так или иначе, один проект не будет иметь ничего общего с другим, поскольку они могут работать параллельно. Несмотря на это, любой код, принадлежащий основной системе, будет наследовать класс C_Mouse до тех пор, пока код, расширяющий данный класс, не будет признан достаточно стабильным и интересным, чтобы мы его использовали в финальном проекте.

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

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


Дополнения к классу C_Terminal

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

inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor, const int zOrder = -1)
   {
      ObjectCreate(m_Infos.ID, szName, obj, 0, 0, 0);
      ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
   }

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


Первый путь: Использование наследования

Чтобы идти первым путем, мы используем наследование, но не от класса C_Mouse, а от класса C_Study, который мы рассматривали в предыдущей статье. Наш заголовочный файл C_StudyS2.mqh будет содержать следующий код:

//+------------------------------------------------------------------+
#include "C_StudyS1.mqh"
//+------------------------------------------------------------------+

// ... Definições locais ....

//+------------------------------------------------------------------+

// ... Alias Locais ...

//+------------------------------------------------------------------+
class C_StudyS2 : public C_StudyS1
{
   protected:
   private :

// ... Código e procedimentos internos ...

//+------------------------------------------------------------------+
   public  :
//+------------------------------------------------------------------+
      C_StudyS2(C_Mouse *arg, color corP, color corN)
         :C_StudyS1(arg, corP, corN)
      {                               
// ... Código interno ....

      }
//+------------------------------------------------------------------+
virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
      {
         double v1, v2;
         int w, h;
         string sz1;
                                
         C_StudyS1::DispatchMessage(id, lparam, dparam, sparam);

// ... Código interno ...

      }
//+------------------------------------------------------------------+
};

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

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

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS2 *extra = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if (mouse.GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

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


Второй путь: Использование указателей.

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

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra1;
   delete extra2;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra1).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra1).DispatchMessage(id, lparam, dparam, sparam);
   (*extra2).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

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

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


Давайте проанализируем код класса C_StudyS2

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

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "..\C_Mouse.mqh"
#include "..\..\..\Service Graphics\Support\Interprocess.mqh"
//+------------------------------------------------------------------+
#define def_ExpansionPrefix "Expansion2_"
#define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
#define def_ExpansionFibo def_ExpansionPrefix + "FB"
//+------------------------------------------------------------------+
#define def_InfoTerminal (*mouse).GetInfoTerminal()
#define def_InfoMousePos (*mouse).GetInfoMouse().Position
//+------------------------------------------------------------------+
class C_StudyS2
{
   protected:
   private :
//+------------------------------------------------------------------+
      C_Mouse *mouse;
//+------------------------------------------------------------------+
      struct st00
      {
         bool            ExecStudy,
                         ClearStudy;
         double          MemPrice;
         datetime        MemDT;
         color           corP,
                         corN;
       }m_Info;
//+------------------------------------------------------------------+

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

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

#define def_FontName "Lucida Console"
#define def_FontSize 10
       void GetDimensionText(const string szArg, int &w, int &h)
          {
             TextSetFont(def_FontName, -10 * def_FontSize, FW_NORMAL);
             TextGetSize(szArg, w, h);
             h += 5;
             w += 5;
          }
//+------------------------------------------------------------------+
       void CreateBTNInfo(int x, int w, int h, string szName, color backColor)
          {
             (*mouse).CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
             ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, def_FontName);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, def_FontSize);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
          }
#undef def_FontSize
#undef def_FontName

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

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

void CreateStudy(void)
   {
      const double FiboLevels[] = {0, 0.236, 0.382, 0.50, 0.618, 1, 1.618, 2};
      ENUM_LINE_STYLE ls;
      color cor;
                                
      ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TB");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TI");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TT");
      (*mouse).CreateObjectGraphics(def_ExpansionFibo, OBJ_FIBO, clrNONE);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_HIDDEN, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_RAY_LEFT, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELS, ArraySize(FiboLevels));
      for (int c0 = 0, c1 = ArraySize(FiboLevels); c0 < c1; c0++)
      {
         ls = ((FiboLevels[c0] == 0) || (FiboLevels[c0] == 1) || (FiboLevels[c0] == 2)  ? STYLE_SOLID : STYLE_DASHDOT);
         ls = (FiboLevels[c0] == 0.5 ? STYLE_DOT : ls);
         switch (ls)
         {
            case STYLE_DOT    : cor = clrBlueViolet;  break;
            case STYLE_DASHDOT: cor = clrViolet;      break;
            default           : cor = clrIndigo;
         }
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELSTYLE, c0, ls);
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELCOLOR, c0, cor);                                  
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELWIDTH, c0, 1);
         ObjectSetString(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELTEXT, c0, (string)NormalizeDouble(FiboLevels[c0] * 100, 2));
      }
      ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 1, m_Info.MemPrice = def_InfoMousePos.Price);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 1, m_Info.MemDT = def_InfoMousePos.dt);
      CreateBTNInfo(def_InfoMousePos.X, 50, 18, def_ExpansionBtn1, clrNONE);
      m_Info.ExecStudy = true;
      m_Info.ClearStudy = false;
   }

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

  1. В первой части мы удалили "нежелательные" объекты, созданные классом C_Mouse при получении от платформы события, указывающего на то, что пользователь начал исследование на графике актива. При удалении данных предметов будьте осторожны и не удаляйте то, что не является действительно незаменимым. Таким образом, мы можем создать совершенно особое исследование, убрав из него всё, что не нужно видеть в том исследовании, которое мы собираемся здесь создать. Обратите внимание, что мы удалили объект старого исследования, и это было сделано для того, чтобы мы могли провести исследование на основе конкретных критериев. Причиной этого может быть также то, что мы хотим использовать комбинацию клавиш для создания исследования на вариации объекта OBJ_FIBO. Этими вариантами могут быть OBJ_FIBOTIMES, OBJ_FIBOFAN, OBJ_FIBOARC, OBJ_FIBOCHANNEL и OBJ_EXPANSION. Все они следуют одним и тем же принципам, которые показаны здесь.
  2. Во второй части мы создаем и определяем свойства объекта. Вот несколько интересных моментов: именно на данном этапе мы сообщаем платформе, что объект будет виден в списке объектов. Здесь мы указываем, какие уровни будет содержать объект. Правда, здесь мы использовали статические уровни, но ничто не мешает вам использовать динамические уровни в своей системе или применять другие уровни. В этом отделе мы расскажем, какими будут все уровни, как в цвете, так и в виде линий, которые будут использоваться для их построения. Можно внести изменения по нашему усмотрению, чтобы получить соответствующий уровень визуализации, потому что, когда мы проводим исследование, мы хотим, чтобы оно было понято быстро, чтобы мы могли извлечь из него пользу.
  3. И в третьей, последней части, мы начинаем построить объект непосредственно на графике, то есть начинаем рисовать. Мы также расскажем о том, что происходит с помощью переменных. Это необходимо для того, чтобы во время процедуры, которую мы увидим позже, всё было сделано правильно.

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

C_StudyS2(C_Mouse *arg, color corP, color corN)
   {                               
      mouse = arg;
      ZeroMemory(m_Info);
      m_Info.corP = corP;
      m_Info.corN = corN;
   }
//+------------------------------------------------------------------+
~C_StudyS2()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_ExpansionPrefix);
   }

Посмотрите внимательно на эти две процедуры. Цель - использовать систему, основанную на втором пути. Чтобы использовать модель наследования, необходимо добавить строки, используемые в теме о первом пути. То же самое нужно сделать в последней процедуре, которую мы видели в классе. Именно это позволяет взаимодействовать с платформой. Взгляните на полный код данной процедуры, который, собственно, и позволяет создать исследование в приведенном ниже фрагменте:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      double v1, v2;
      int w, h;
      string sz1;
                                
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) && (m_Info.ExecStudy)) m_Info.ClearStudy = true;
            break;
         case CHARTEVENT_MOUSE_MOVE:
            if (mouse.GetInfoMouse().ExecStudy)
            {
               if (!m_Info.ExecStudy) CreateStudy();
               v1 = def_InfoMousePos.Price - m_Info.MemPrice;
               v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
               sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);
               GetDimensionText(sz1, w, h);
               ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 0, def_InfoMousePos.Price);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 0, def_InfoMousePos.dt);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetString(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_TEXT, sz1);                                                                                                                             
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XSIZE, w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YSIZE, h);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XDISTANCE, def_InfoMousePos.X - w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, def_InfoMousePos.Y - (v1 < 0 ? 1 : h));
            }else if (m_Info.ExecStudy)
            {
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, clrNONE);
               ObjectDelete(def_InfoTerminal.ID, def_ExpansionBtn1);
               if (m_Info.ClearStudy) ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
               m_Info.ExecStudy = false;
            }
            break;
         }
      }

Интересный код, не правда ли? Обратите внимание, что мы ни в коем случае не смотрим на действия мыши. Мы рассматриваем то, что делается в классе C_Mouse. Это будет сделано, когда мы рассмотрим эти доказательства с этого момента. Пока класс C_Mouse указывает на то, что мы проводим какое-то исследование, данный класс будет следовать этому направлению, таким образом, выполняя исследование в соответствии с указаниями класса C_Mouse. Как только класс C_Mouse перестанет использоваться в исследовании, мы удалим объект, который использовался для размещения информационного текста. Но если во время исследования нажать клавишу ESC, то объект исследования также будет удален. Можно заметить, что размеры объекта, используемого для передачи текста, рассчитываются динамически, то есть он может быть больше или меньше в зависимости от случая, и всё это контролируется в этой области, а также вопрос цветов и размещения объектов.

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

v1 = def_InfoMousePos.Price - m_Info.MemPrice;
v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);

Чтобы понять эти 3 линии факторизации и форматирования информации для представления, необходимо иметь возможность видеть, что система динамически адаптируется к активу, на котором выполняется код. Для представления значений некоторых активов может потребоваться 4 символа, а для других 5. В нескольких фондовых активах мы будем иметь активы, которые требуют 2 символа. Чтобы сделать систему легко адаптируемой, мы используем вид моделирования, о котором говорилось выше. Звучит непонятно, но на самом деле это просто необычно.

Для начала мы учитываем разницу между ценой, по которой началось исследование, и ценовой линией. Таким образом, мы получим значение в пунктах или в финансовой стоимости; это значение смещения между позицией, с которой мы начали исследование, и текущей позицией, в которой находится мышь. Чтобы правильно представить его, нам нужно знать, сколько символов необходимо. Для этого мы используем следующий способ: Мы знаем, что с помощью символа процента (%) можно определить тип информации, которая будет преобразована в строку. При следующем формате < %.2f> мы получим значение, содержащее два знака после запятой, при <%.4f> - значение, содержащее 4 знака после запятой, и так далее. Но нам нужно, чтобы это определилось в RUN-TIME

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

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

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

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


Заключение

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

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

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


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

Прикрепленные файлы |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Hilario Miguel Ofarril Gonzalez
Hilario Miguel Ofarril Gonzalez | 4 мар. 2024 в 05:53
Очень точно и конкретно
Разработка системы репликации (Часть 31): Проект советника — класс C_Mouse (V) Разработка системы репликации (Часть 31): Проект советника — класс C_Mouse (V)
Разрабатывать способ установки таймера необходимо таким образом, чтобы во время репликации/моделирования он мог сообщить нам, сколько времени осталось, что может показаться на первый взгляд простым и быстрым решением. Многие просто пытаются приспособиться и использовать ту же систему, что и в случае с торговым сервером. Но есть один момент, который многие не учитывают, когда думают о таком решении: при репликации, и это не говоря уже о моделировании, часы работают по-другому. Всё это усложняет создание подобной системы.
Популяционные алгоритмы оптимизации: Устойчивость к застреванию в локальных экстремумах (Часть I) Популяционные алгоритмы оптимизации: Устойчивость к застреванию в локальных экстремумах (Часть I)
Эта статья представляет уникальный эксперимент, цель которого - исследовать поведение популяционных алгоритмов оптимизации в контексте их способности эффективно покидать локальные минимумы при низком разнообразии в популяции и достигать глобальных максимумов. Работа в этом направлении позволит глубже понять, какие конкретные алгоритмы могут успешно продолжать поиск из координат, установленных пользователем в качестве отправной точки, и какие факторы влияют на их успешность в этом процессе.
Разработка системы репликации (Часть 32): Система ордеров (I) Разработка системы репликации (Часть 32): Система ордеров (I)
Из всего, что было разработано до настоящего момента, данная система, как вы наверняка заметите и со временем согласитесь, - является самым сложным. Сейчас нам нужно сделать нечто очень простое: заставить нашу систему имитировать работу торгового сервера на практике. Эта необходимость точно реализовывать способ моделирования действий торгового сервера кажется простым делом. По крайней мере, на словах. Но нам нужно сделать это так, чтобы для пользователя системы репликации/моделирования всё происходило как можно более незаметно или прозрачно.
Показатель склонности (Propensity score) в причинно-следственном выводе Показатель склонности (Propensity score) в причинно-следственном выводе
В статье рассматривается тема матчинга в причинно-следственном выводе. Матчинг используется для сопоставления похожих наблюдений в наборе данных. Это необходимо для правильного определения каузальных эффектов, избавления от предвзятости. Автор рассказывает, как это помогает в построении торговых систем на машинном обучении, которые становятся более устойчивыми на новых данных, на которых не обучались. Центральная роль отводится показателю склонности, который широко используется в причинно-следственном выводе.