Español Português
preview
Моделирование рынка (Часть 02): Кросс-ордера (II)

Моделирование рынка (Часть 02): Кросс-ордера (II)

MetaTrader 5Тестер |
87 0
Daniel Jose
Daniel Jose

Введение

В предыдущей статье Моделирование рынка (Часть 01): Кросс-ордера (I), мы изучали альтернативу для довольно распространенной проблемы, особенно среди тех, кто торгует фьючерсными контрактами. Хотя мы не показали окончательного решения, так как вся статья была посвящена только индикатору Chart Trade, содержание статьи имеет огромное значение, так как позволяет нам сделать класс C_Terminal способным предоставить нам подходящее название для торговли такими фьючерсными контрактами.

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

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

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


Понимание проблем

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

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

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

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

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

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

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

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

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


Пойти на уступки

Изменения, которые будут произведены здесь, останутся в том виде, в котором они были внесены. Другими словами, код будет двигаться не назад, а вперед. Однако такие уступки откроют возможность использовать некоторые вещи, которые раньше были невозможны. Поэтому нужно использовать эти новые возможности с осторожностью.

Первое изменение можно увидеть в файле C_Terminal.mqh. Ниже можно ознакомиться с новым кодом в полном объеме.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+
010.    protected:
011.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
012. //+------------------------------------------------------------------+
013.       struct st_Terminal
014.       {
015.          ENUM_SYMBOL_CHART_MODE   ChartMode;
016.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
017.          long           ID;
018.          string         szSymbol;
019.          int            Width,
020.                         Height,
021.                         nDigits,
022.                         SubWin,
023.                         HeightBar;
024.          double         PointPerTick,
025.                         ValuePerPoint,
026.                         VolumeMinimal,
027.                         AdjustToTrade;
028.       };
029. //+------------------------------------------------------------------+
030.       void CurrentSymbol(bool bUsingFull)
031.          {
032.             MqlDateTime mdt1;
033.             string sz0, sz1;
034.             datetime dt = macroGetDate(TimeCurrent(mdt1));
035.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
036.       
037.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
038.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
039.             switch (eTS)
040.             {
041.                case DOL   :
042.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
043.                case IND   :
044.                case WIN   : sz1 = "GJMQVZ";       break;
045.                default    : return;
046.             }
047.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
048.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
049.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
050.          }
051. //+------------------------------------------------------------------+
052.    private   :
053.       st_Terminal m_Infos;
054.       struct mem
055.       {
056.          long    Show_Descr,
057.                Show_Date;
058.          bool   AccountLock;
059.       }m_Mem;
060. //+------------------------------------------------------------------+
061. inline void ChartChange(void)
062.          {
063.             int x, y, t;
064.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
065.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
066.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
067.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
068.             m_Infos.HeightBar = (int)((t - y) / 100);
069.          }
070. //+------------------------------------------------------------------+
071.    public   :
072. //+------------------------------------------------------------------+      
073.       C_Terminal(const long id = 0, const uchar sub = 0)
074.          {
075.             m_Infos.ID = (id == 0 ? ChartID() : id);
076.             m_Mem.AccountLock = false;
077.             m_Infos.SubWin = (int) sub;
078.             CurrentSymbol(false);
079.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
080.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
081.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
082.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
083.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
084.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
085.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
086.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
087.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
088.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
089.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
090.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
091.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
092.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
093.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
094.             ChartChange();
095.          }
096. //+------------------------------------------------------------------+
097.       ~C_Terminal()
098.          {
099.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
100.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
101.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
102.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
103.          }
104. //+------------------------------------------------------------------+
105. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
106.          {
107.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
108.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
109.          }
110. //+------------------------------------------------------------------+
111. inline const st_Terminal GetInfoTerminal(void) const
112.          {
113.             return m_Infos;
114.          }
115. //+------------------------------------------------------------------+
116. const double AdjustPrice(const double arg) const
117.          {
118.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
119.          }
120. //+------------------------------------------------------------------+
121. inline datetime AdjustTime(const datetime arg)
122.          {
123.             int nSeconds= PeriodSeconds();
124.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
125.             
126.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
127.          }
128. //+------------------------------------------------------------------+
129. inline double FinanceToPoints(const double Finance, const uint Leverage)
130.          {
131.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
132.             
133.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
134.          };
135. //+------------------------------------------------------------------+
136.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
137.          {
138.             static string st_str = "";
139.             
140.             switch (id)
141.             {
142.                case CHARTEVENT_CHART_CHANGE:
143.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
144.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
145.                   ChartChange();
146.                   break;
147.                case CHARTEVENT_OBJECT_CLICK:
148.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
149.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
150.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
151.                   break;
152.                case CHARTEVENT_OBJECT_CREATE:
153.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
154.                   st_str = sparam;
155.                   break;
156.             }
157.          }
158. //+------------------------------------------------------------------+
159. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const
160.          {
161.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false);
162.             ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0);
163.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
164.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
165.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
166.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
167.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
168.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
169.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
170.          }
171. //+------------------------------------------------------------------+
172.       bool IndicatorCheckPass(const string szShortName)
173.          {
174.             string szTmp = szShortName + "_TMP";
175.             
176.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
177.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
178.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
179.             {
180.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
181.                Print("Only one instance is allowed...");
182.                SetUserError(C_Terminal::ERR_NoMoreInstance);
183.                
184.                return false;
185.             }
186.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
187.    
188.             return true;
189.          }
190. //+------------------------------------------------------------------+
191. };

Исходный код для файла C_Terminal.mqh

Прошу заметить, что изменения были минимальными. Фактически, по отношению к коду, рассмотренному в предыдущей статье, мы просто открыли возможность вызова процедуры CurrentSymbol внутри других классов, наследующих класс C_Terminal. Раньше данная процедура была эксклюзивной для класса, но теперь мы перевели ее в защищенный режим. Мы могли бы сделать его непосредственно публичным, но мне не нравятся такие радикальные изменения. Я предпочитаю предоставлять минимальные привилегии, пока программа не покажет необходимость в большем. Как защищенная процедура, она не может быть доступна никаким образом.

В дополнение к этому небольшому изменению было сделано еще одно. Обратите внимание, что в конструкторе в строке 73 мы удалили параметр, добавленный в предыдущей статье. Однако в строке 78 мы продолжили принудительное использование мини-контракта. Подобное решение открывает предпосылки для создания новых функциональных возможностей. Причина в том, что при сохранении прежней структуры мы были бы вынуждены закрыть Chart Trade, чтобы обновить контракт, который отображается в индикаторе. Таким образом, нам не придется делать это снова. Мы можем создать сообщение, позволяющее изменить контракт. Однако, из-за этого изменения, появятся и другие, как описано в предыдущей статье. Это предусмотрено в коде Chart Trade. Однако сейчас мы не будем об этом беспокоиться. Сначала нам нужно кое-что сделать.

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

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

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

Чтобы понять, как будет происходить этот обмен, нужно понять, что после инициализации выполняется OnChartEvent, когда что-то помещается на график. Знаете ли вы, какое событие запускается, чтобы OnChartEvent выполнялся, когда что-то помещается на график? Если найдете эту информацию, то увидите, что когда на график помещается какой-либо индикатор или советник, MetaTrader 5 запускает событие CHARTEVENT_CHART_CHANGE. Понимать это очень важно, так как мы будем использовать именно данное событие, чтобы всё работало.

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


Реализуем решение проблемы

Во-первых, в нашу систему необходимо добавить три новых события. Их можно увидеть в коде файла Defines.mqh, который полностью представлен ниже:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.    #define macro_DEBUG_MODE(A) \
08.                Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.    #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay         "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService      0xFED00000
16. #define def_IndicatorTimeFrame   (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame         4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.    double    dValue;
22.    long      _long;                                 // 1 Information
23.    datetime _datetime;                              // 1 Information
24.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents    {
30.          evHideMouse,                  //Hide mouse price line
31.          evShowMouse,                  //Show mouse price line
32.          evHideBarTime,                //Hide bar time
33.          evShowBarTime,                //Show bar time
34.          evHideDailyVar,               //Hide daily variation
35.          evShowDailyVar,               //Show daily variation
36.          evHidePriceVar,               //Hide instantaneous variation
37.          evShowPriceVar,               //Show instantaneous variation
38.          evCtrlReplayInit,             //Initialize replay control
39.          evChartTradeBuy,              //Market buy event
40.          evChartTradeSell,             //Market sales event 
41.          evChartTradeCloseAll,         //Event to close positions
42.          evChartTrade_At_EA,           //Event to communication
43.          evEA_At_ChartTrade            //Event to communication
44.                   };
45. //+------------------------------------------------------------------+

Исходный код файла Defines.mqh

Здесь добавлены строки 42 и 43, которые почти не требуют объяснений, так как секрет заключается в направлении связи. То есть строка 42 относится к сообщению, идущему от Chart Trade к советнику. Внимание: не путайте данное сообщение с торговыми событиями. Это событие будет направлено на другой тип общения, как если бы речь шла о специальном канале.

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

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.81"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12537"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
12. //+------------------------------------------------------------------+
13. #define def_ShortName "Indicator Chart Trade"
14. //+------------------------------------------------------------------+
15. C_ChartFloatingRAD *chart = NULL;
16. //+------------------------------------------------------------------+
17. input ushort         user01 = 1;         //Leverage
18. input double         user02 = 100.1;     //Finance Take
19. input double         user03 = 75.4;      //Finance Stop
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03);
24.    
25.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
26. 
27.    return INIT_SUCCEEDED;
28. }
29. //+------------------------------------------------------------------+
30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
31. {
32.    return rates_total;
33. }
34. //+------------------------------------------------------------------+
35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
36. {
37.    if (_LastError < ERR_USER_ERROR_FIRST) 
38.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
39. }
40. //+------------------------------------------------------------------+
41. void OnDeinit(const int reason)
42. {
43.    switch (reason)
44.    {
45.       case REASON_INITFAILED:
46.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
47.          break;
48.       case REASON_CHARTCHANGE:
49.          (*chart).SaveState();
50.          break;
51.    }
52. 
53.    delete chart;
54. }
55. //+------------------------------------------------------------------+

Исходный код индикатора Chart Trade

Хорошо, но где новые события? Я ожидал их увидеть в процедуре OnChartEvent. На самом деле они там есть, но, чтобы упростить ситуацию, всё рассматривается в одном месте, то есть в процедуре DispatchMessage. И данная процедура находится в классе C_ChartFloatingRAD, который можно увидеть в полном объеме чуть ниже.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Terminal
015. {
016.    private   :
017.       enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
018.       struct st00
019.       {
020.          short    x, y, minx, miny,
021.                   Leverage;
022.          string   szObj_Chart,
023.                   szObj_Editable,
024.                   szFileNameTemplate;
025.          long     WinHandle;
026.          double   FinanceTake,
027.                   FinanceStop;
028.          bool     IsMaximized,
029.                   IsDayTrade,
030.                   IsSaveState;
031.          struct st01
032.          {
033.             short  x, y, w, h;
034.             color  bgcolor;
035.             int    FontSize;
036.             string FontName;
037.          }Regions[MSG_NULL];
038.       }m_Info;
039.       struct st01
040.       {
041.          short     y[2];
042.          bool      bOk;
043.       }m_Init;
044.       C_Mouse       *m_Mouse;
045. //+------------------------------------------------------------------+
046.       void CreateWindowRAD(int w, int h)
047.          {
048.             m_Info.szObj_Chart = "Chart Trade IDE";
049.             m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit";
050.             ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
051.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
052.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
053.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
054.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
055.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
057.             m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
058.          };
059. //+------------------------------------------------------------------+
060.       void AdjustEditabled(C_AdjustTemplate &Template, bool bArg)
061.          {
062.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++)
063.                if (bArg)
064.                {
065.                   Template.Add(EnumToString(c0), "bgcolor", NULL);
066.                   Template.Add(EnumToString(c0), "fontsz", NULL);
067.                   Template.Add(EnumToString(c0), "fontnm", NULL);
068.                }
069.                else
070.                {
071.                   m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor"));
072.                   m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz"));
073.                   m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm");
074.                }
075.          }
076. //+------------------------------------------------------------------+
077. inline void AdjustTemplate(const bool bFirst = false)
078.          {
079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
080.             
081.             C_AdjustTemplate    *Template;
082.             
083.             if (bFirst)
084.             {
085.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
086.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
087.                {
088.                   (*Template).Add(EnumToString(c0), "size_x", NULL);
089.                   (*Template).Add(EnumToString(c0), "size_y", NULL);
090.                   (*Template).Add(EnumToString(c0), "pos_x", NULL);
091.                   (*Template).Add(EnumToString(c0), "pos_y", NULL);
092.                }
093.                AdjustEditabled(Template, true);
094.             }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
095.             if (_LastError >= ERR_USER_ERROR_FIRST)
096.             {
097.                delete Template;
098.                
099.                return;
100.             }
101.             m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
102.             m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
103.             m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
104.             (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
105.             (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
106.             (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
107.             (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
108.             (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
109.             (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
110.             if (!(*Template).Execute())
111.             {
112.                delete Template;
113. 
114.                macro_CloseIndicator(C_Terminal::ERR_FileAcess);
115.             };
116.             if (bFirst)
117.             {
118.                for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
119.                {
120.                   m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x"));
121.                   m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y"));
122.                   m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x"));
123.                   m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y"));
124.                }
125.                m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
126.                AdjustEditabled(Template, false);
127.             };
128.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6));
129.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
130.             ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
131. 
132.             delete Template;
133.             
134.             ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
135.             ChartRedraw(m_Info.WinHandle);
136. 
137. #undef macro_PointsToFinance
138.          }
139. //+------------------------------------------------------------------+
140.       eObjectsIDE CheckMousePosition(const short x, const short y)
141.          {
142.             int xi, yi, xf, yf;
143.             
144.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
145.             {
146.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
147.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
148.                xf = xi + m_Info.Regions[c0].w;
149.                yf = yi + m_Info.Regions[c0].h;
150.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
151.             }
152.             return MSG_NULL;
153.          }
154. //+------------------------------------------------------------------+
155. inline void DeleteObjectEdit(void)
156.          {
157.             ChartRedraw();
158.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable);
159.          }
160. //+------------------------------------------------------------------+
161.       template <typename T >
162.       void CreateObjectEditable(eObjectsIDE arg, T value)
163.          {
164.             long id = GetInfoTerminal().ID;
165.             
166.             DeleteObjectEdit();
167.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
168.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3);
169.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
170.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
171.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
172.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
173.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
174.             ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
175.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
176.             ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
177.             ChartRedraw();
178.          }
179. //+------------------------------------------------------------------+
180.       bool RestoreState(void)
181.          {
182.             uCast_Double info;
183.             bool bRet;
184.             C_AdjustTemplate *Template;
185.             
186.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
187.             {
188.                m_Info.x = (short) info._16b[0];
189.                m_Info.y = (short) info._16b[1];
190.                m_Info.minx = (short) info._16b[2];
191.                m_Info.miny = (short) info._16b[3];
192.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl");
193.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
194.                {
195.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
196.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
197.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
198.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
199.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
200.                   if (!(*Template).Execute()) bRet = false; else
201.                   {
202.                      m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
203.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
204.                      m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
205.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
206.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
207.                   }
208.                };               
209.                delete Template;
210.             };
211.             
212.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
213.             
214.             return bRet;
215.          }
216. //+------------------------------------------------------------------+
217.    public   :
218. //+------------------------------------------------------------------+
219.       C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop)
220.          :C_Terminal(0)
221.          {
222.             m_Mouse = MousePtr;
223.             m_Info.IsSaveState = false;
224.             if (!IndicatorCheckPass(szShortName)) return;
225.             if (!RestoreState())
226.             {
227.                m_Info.Leverage = Leverage;
228.                m_Info.IsDayTrade = true;
229.                m_Info.FinanceTake = FinanceTake;
230.                m_Info.FinanceStop = FinanceStop;
231.                m_Info.IsMaximized = true;
232.                m_Info.minx = m_Info.x = 115;
233.                m_Info.miny = m_Info.y = 64;
234.             }
235.             m_Init.y[false] = 150;
236.             m_Init.y[true] = 210;
237.             CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]);
238.             AdjustTemplate(true);
239.          }
240. //+------------------------------------------------------------------+
241.       ~C_ChartFloatingRAD()
242.          {
243.             ChartRedraw();
244.             ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart);
245.             if (!m_Info.IsSaveState)
246.                FileDelete(m_Info.szFileNameTemplate);
247.                         
248.             delete m_Mouse;
249.          }
250. //+------------------------------------------------------------------+
251.       void SaveState(void)
252.          {
253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B);
254.             
255.             uCast_Double info;
256.             
257.             info._16b[0] = m_Info.x;
258.             info._16b[1] = m_Info.y;
259.             info._16b[2] = m_Info.minx;
260.             info._16b[3] = m_Info.miny;
261.             macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue);
262.             m_Info.IsSaveState = true;
263.             
264. #undef macro_GlobalVariable
265.          }
266. //+------------------------------------------------------------------+
267.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
268.          {
269. #define macro_AdjustMinX(A, B)    {                          \
270.             B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x;   \
271.             mx = x - m_Info.Regions[MSG_TITLE_IDE].w;        \
272.             A = (B ? (mx > 0 ? mx : 0) : A);                 \
273.                                  }
274. #define macro_AdjustMinY(A, B)   {                           \
275.             B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y;   \
276.             my = y - m_Info.Regions[MSG_TITLE_IDE].h;        \
277.             A = (B ? (my > 0 ? my : 0) : A);                 \
278.                                  }
279.                               
280.             static short sx = -1, sy = -1, sz = -1;
281.             static eObjectsIDE obj = MSG_NULL;
282.             short   x, y, mx, my;
283.             double dvalue;
284.             bool b1, b2, b3, b4;
285.             ushort ev = evChartTradeCloseAll;
286.    
287.             switch (id)
288.             {
289.                case CHARTEVENT_CUSTOM + evEA_At_ChartTrade:
290.                   if (m_Init.bOk = ((lparam >= 0) && (lparam < 2)))
291.                      CurrentSymbol((bool)lparam);
292.                   AdjustTemplate(true);
293.                   break;
294.                case CHARTEVENT_CHART_CHANGE:
295.                   if (!m_Init.bOk)
296.                      EventChartCustom(GetInfoTerminal().ID, evChartTrade_At_EA, 0, 0, "");
297.                   x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS);
298.                   y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS);
299.                   macro_AdjustMinX(m_Info.x, b1);
300.                   macro_AdjustMinY(m_Info.y, b2);
301.                   macro_AdjustMinX(m_Info.minx, b3);
302.                   macro_AdjustMinY(m_Info.miny, b4);
303.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
304.                   break;
305.                case CHARTEVENT_MOUSE_MOVE:
306.                   if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft))
307.                   {                  
308.                      switch (CheckMousePosition(x = (short)lparam, y = (short)dparam))
309.                      {
310.                         case MSG_MAX_MIN:
311.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
312.                            break;
313.                         case MSG_DAY_TRADE:
314.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
315.                            break;
316.                         case MSG_LEVERAGE_VALUE:
317.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
318.                            break;
319.                         case MSG_TAKE_VALUE:
320.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
321.                            break;
322.                         case MSG_STOP_VALUE:
323.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
324.                            break;
325.                         case MSG_TITLE_IDE:
326.                            if (sx < 0)
327.                            {
328.                               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
329.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
330.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
331.                            }
332.                            if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
333.                            if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
334.                            if (m_Info.IsMaximized)
335.                            {
336.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
337.                               m_Info.y = (my > 0 ? my : m_Info.y);
338.                            }else
339.                            {
340.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
341.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
342.                            }
343.                            break;
344.                         case MSG_BUY_MARKET:
345.                            ev = evChartTradeBuy;
346.                         case MSG_SELL_MARKET:
347.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
348.                         case MSG_CLOSE_POSITION:
349.                            if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) //<<
350.                            {
351.                               string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
352.                                                           m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
353.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
354.                               EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp);
355.                            }
356.                            break;
357.                      }
358.                      if (sz < 0)
359.                      {
360.                         sz = x;
361.                         AdjustTemplate();
362.                         if (obj == MSG_NULL) DeleteObjectEdit();
363.                      }
364.                   }else
365.                   {
366.                      sz = -1;
367.                      if (sx > 0)
368.                      {
369.                         ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                  
370.                         sx = sy = -1;
371.                      }
372.                   }
373.                   break;
374.                case CHARTEVENT_OBJECT_ENDEDIT:
375.                   switch (obj)
376.                   {
377.                      case MSG_LEVERAGE_VALUE:
378.                      case MSG_TAKE_VALUE:
379.                      case MSG_STOP_VALUE:
380.                         dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT));
381.                         if (obj == MSG_TAKE_VALUE)
382.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
383.                         else if (obj == MSG_STOP_VALUE)
384.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
385.                         else
386.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
387.                         AdjustTemplate();
388.                         obj = MSG_NULL;
389.                         ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable);
390.                         break;
391.                   }
392.                   break;
393.                case CHARTEVENT_OBJECT_DELETE:
394.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
395.                   break;
396.             }
397.             ChartRedraw();
398.          }
399. //+------------------------------------------------------------------+
400. };
401. //+------------------------------------------------------------------+
402. #undef macro_NameGlobalVariable
403. #undef macro_CloseIndicator
404. //+------------------------------------------------------------------+

Исходный код файла C_ChartFloatingRAD.mqh

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

Давайте посмотрим, что на самом деле изменилось. Это не связано с предыдущей статьей, а в подтверждение того, что можно увидеть на видео, доступном в конце этой статьи. Первое, что следует отметить, - это появление небольшой структуры в строке 39. Внимательно следите за объяснением, иначе вы не поймете, что показано в видео. В этой структуре есть строка 41. Она содержит два значения для размера окна, которое на самом деле является объектом OBJ_CHART. Не волнуйтесь, скоро вы всё поймете. То же самое относится и к переменной в строке 42, которая вместе с этим массивом из двух единиц делает возможным то, что показано на видео.

Затем мы перейдем к конструктору класса. Он начинается со строки 219, и, как видите, здесь нет ни лишнего параметра из предыдущей статьи, ни строки 220, в которой мы вызывали конструктор класса C_Terminal. Иными словами, в данном случае мы возвращаемся к теме предыдущей статьи. Теперь обратите внимание на следующее: окно Chart Trade является объектом OBJ_CHART и его можно свернуть или развернуть. Это уже реализовано. Однако, когда советника нет на графике, на котором находится Chart Trade, наше желание, - чтобы кнопки были скрыты.

Для этого нам нужно изменить размер в направлении Y системы координат. В настоящее время эти размеры определяются в строках 235 и 236. Теперь внимание: если значение переменной m_Init.bOk равно false, значит что-то работает неправильно. В этом случае мы скрываем кнопки взаимодействия, чтобы пользователь или трейдер не подумал, что они отправляют ордера на сервер. Чтобы произошло сокрытие, значение координаты Y должно быть равно 150. Когда значение m_Init.bOk равно true, это означает, что пользователь или трейдер может отправлять ордера через советник. Таким образом, значение координаты Y будет равно 210, что соответствует предыдущему значению по умолчанию, согласно вызову в строке 237.

Прошу заметить, что строка 237, которая отвечает за создание объекта OBJ_CHART, содержащего Chart Trade, отличается. Обратите внимание, что вместо того, чтобы поместить значение 210 в качестве размера, мы используем доступ к значению, определенному в массиве. В то же время мы устанавливаем значение m_Init.bOk в false. Другими словами, мы фактически приказываем объекту OBJ_CHART быть построенным со значением 150. Но почему бы не использовать данное значение с самого начала? Прежде всего, потому что мы хотим протестировать и убедиться, что код действительно работает так, как ожидалось. Если бы передавали значение 150 напрямую, не так, как это делается сейчас, у нас не было бы уверенности в том, что моделирование работает. Однако таким образом мы можем гарантировать, что оно работает.

Еще один момент: это происходит в процедуре AdjustTemplate, вызываемой сразу после нее, код которой находится в строке 77. В этой процедуре единственное изменение было сделано в строке 128, потому что, когда мы разворачиваем или скрываем Chart Trade, мы изменяем значение координаты Y. Поэтому, чтобы Chart Trade оставался в соответствии с тем, что мы видим, нам нужно сделать так, чтобы значения координаты Y соответствовали правилам, указанным в конструкторе класса. Таким образом, даже если мы развернем или скроем Chart Trade, кнопки не будут видны до тех пор, пока всё не будет в порядке для их отображения.

Это было легко. Прошу заметить, что изменения были простыми и их было немного. Теперь мы рассмотрим часть обмена сообщениями, которая также содержит несколько изменений. Затем мы переходим к строке 267, где запускается процедура DispatchMessage. Прежде, чем рассматривать сообщения, давайте перейдем к строке 349, где можно заметить минимальное, но очень важное отличие. Чтобы излишне не усложнять функцию проверки мыши, чтобы знать, где произошел щелчок, мы добавили новое проверяемое значение в этой строке. Если значение переменной m_Init.bOk равно false и мы щелкнем в области, где должны быть кнопки, проверка не вызовет никакого события. Именно данный уровень простоты делает программирование впечатляющим, поскольку многие попытались бы проверить это в другом месте кода, что сделало бы процедуру проверки очень сложной.

Очень хорошо, но давайте вернемся к вопросу о сообщениях. Начнем со следующего: когда индикатор помещается на график, мы знаем, что первым полученным событием будет CHARTEVENT_CHART_CHANGE. Таким образом, в строке 295 мы проверяем, находится ли наша переменная в состоянии false. Если подтверждение получено, мы вызываем пользовательское событие на строке 296. В любом случае, или потому что советник перехватил сработавшее событие, или потому что он недавно был помещен на график, в результате мы получим еще одно событие. Это фиксируется в Chart Trade в строке 289.

Теперь начинается самое интересное. Когда значение, проверенное в строке 290, больше или равно нулю, запускается выполнение строки 291. Обратите внимание на следующее: если значение равно нулю, оно будет равным false, если не нулевое - true. Но если значение больше единицы, принимать его не стоит. Таким образом, советник должен передать значение, равное нулю или единице, которое указывает на то, используем ли мы мини-контракт или полный контракт. Наконец, в строке 292 мы осуществляем запрос на обновление Chart Trade. Это кажется очень сложным, тем более, что у нас может быть значение, отличающееся от нуля и единицы. В этом случае кнопки должны быть скрыты. Это происходит так, потому что советник только что был удален с графика или произошло что-то нестандартное.

Теперь вы, наверное, думаете, что я сошел с ума. «Как получилось, что Chart Trade получил сообщение о том, что советник был удален с графика или произошло что-то странное?» Чтобы понять это, давайте обратимся к теме советника. Информация об этом приведена ниже.


Как получилось так, что советник стал работать?

Итак, ниже можно увидеть код советника для демо-версии.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Virtual Test..."
04. #property description "Demo version between interaction"
05. #property description "of Chart Trade and Expert Advisor"
06. #property version   "1.81"
07. #property link "https://www.mql5.com/pt/articles/12537"
08. //+------------------------------------------------------------------+
09. #include <Market Replay\Defines.mqh>
10. //+------------------------------------------------------------------+
11. class C_Decode
12. {
13.    private   :
14.       struct stInfoEvent
15.       {
16.          EnumEvents  ev;
17.          string      szSymbol,
18.                      szContract;
19.          bool        IsDayTrade;
20.          ushort      Leverange;
21.          double      PointsTake,
22.                      PointsStop;
23.       }info[1];
24.    public   :
25. //+------------------------------------------------------------------+
26.       C_Decode()
27.          {
28.             info[0].szSymbol = _Symbol;
29.          }
30. //+------------------------------------------------------------------+   
31.       bool Decode(const int id, const string sparam)
32.       {
33.          string Res[];
34.       
35.          if (StringSplit(sparam, '?', Res) != 7) return false;
36.          stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
37.          if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc;
38.          
39.          ArrayPrint(info, 2);
40.       
41.          return true;
42.       }
43. }*GL_Decode;
44. //+------------------------------------------------------------------+
45. enum eTypeContract {MINI, FULL};
46. //+------------------------------------------------------------------+
47. input eTypeContract user00 = MINI;       //Cross order in contract
48. //+------------------------------------------------------------------+
49. bool bOk;
50. //+------------------------------------------------------------------+
51. int OnInit()
52. {
53.    bOk = false;
54.    GL_Decode = new C_Decode;
55.    
56.    return INIT_SUCCEEDED;
57. }
58. //+------------------------------------------------------------------+
59. void OnTick() {}
60. //+------------------------------------------------------------------+
61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
62. {
63.    switch (id)
64.    {
65.       case CHARTEVENT_CUSTOM + evChartTradeBuy     :
66.       case CHARTEVENT_CUSTOM + evChartTradeSell    :
67.       case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
68.          GL_Decode.Decode(id - CHARTEVENT_CUSTOM, sparam);
69.          break;
70.       case CHARTEVENT_CHART_CHANGE:
71.          if (bOk)   break;
72.       case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
73.          bOk = true;
74.          EventChartCustom(ChartID(), evEA_At_ChartTrade, user00, 0, "");
75.          break;
76.    }
77. }
78. //+------------------------------------------------------------------+
79. void OnDeinit(const int reason)
80. {
81.    switch (reason)
82.    {
83.       case REASON_REMOVE:
84.       case REASON_INITFAILED:
85.          EventChartCustom(ChartID(), evEA_At_ChartTrade, -1, 0, "");
86.          break;
87.    }
88.    delete GL_Decode;
89. }
90. //+------------------------------------------------------------------+

Исходный код советника

Теперь возникает вопрос: как работает данный код? Прежде чем ответить на этот вопрос, давайте посмотрим на небольшое изменение, которое имело место. В предыдущей статье мы сделали так, чтобы Chart Trade указывал, какой контракт он просматривает. Если посмотреть на код Chart Trade, а точнее на код класса C_ChartFloatingRAD, то в строке 351 добавилась новая информация. Данная информация относится к названию контракта и расшифровывается в строке 36 кода советника. Это был небольшой момент, который необходимо было упомянуть. Теперь давайте посмотрим, как работает код советника.

Обратите внимание, что код, позволяющий пользователю или трейдеру указывать тип контракта, который ранее присутствовал в Chart Trade, теперь находится в строках 45 и 47 советника. Однако в этом советнике мы нигде не обращаемся к классу C_Terminal, но почему? Потому что мы еще не подключились к серверу. Советник всё еще находится на стадии демонстрации. Однако обратите внимание, что в строке 49 у нас есть переменная. Это необходимо для того, чтобы любое изменение на графике не вызывало событие в Chart Trade, чтобы сообщить о нем. В строке 53 мы инициализируем данную переменную как false. Уже в строке 73 мы объявляем его как true. И в строке 71 мы проверяем его именно для того, чтобы избежать ненужной отправки сообщений.

Теперь обратите внимание на следующее: как и индикатор, советник имеет в качестве первого события CHARTEVENT_CHART_CHANGE, которое объявлено в строке 70. Поскольку переменная имеет значение false, событие не выполняется. Фактически, мы собираемся выполнить то же самое, что сделали бы, если бы Chart Trade запросил что-то. Другими словами, мы попадаем на строку 72. В результате этого в строке 74 будет инициировано событие, а указанное значение будет равно либо нулю, либо единице. Поэтому перечисление в строке 45 должно следовать за тем, что выводится на экран. Если изменить порядок терминов, результат в Chart Trade будет другим. Вносите изменения здесь только в том случае, если вы знаете, что делаете. В противном случае Chart Trade будет показывать неправильный контракт.

Теперь идет вторая часть, в которой советник сообщает Chart Trade, что он больше не сможет выполнять свою роль. После этого Chart Trade должен скрыть кнопки отправки ордеров. Это произойдет, когда будет выполнена процедура в строке 79. В обычных ситуациях MetaTrader 5 запускает событие DeInit, чтобы закрыть какой-нибудь элемент на графике. Когда это происходит, значение reason содержит причину вызова. Здесь, в строке 81, мы проверяем такие причины. Если они объявлены, то в строке 85 будет вызвано событие. Прошу заметить, что значение после типа события равно -1. Это означает, что советник больше не будет доступен для использования в Chart Trade. Таким образом, элементы управления отправкой ордера будут автоматически скрыты.

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


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

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

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


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

Прикрепленные файлы |
Anexo.zip (490.53 KB)
Нейросети в трейдинге: Декомпозиция вместо масштабирования — Построение модулей Нейросети в трейдинге: Декомпозиция вместо масштабирования — Построение модулей
В этой статье продолжаем практическое знакомство с SSCNN — архитектурным решением нового поколения, способным работать с фрагментированными временными рядами. Вместо слепого масштабирования — разумная модульность, внимание к деталям и точечная нормализация. Мы шаг за шагом создаём вычислительные блоки в среде MQL5 и закладываем основу для надёжного прогнозного анализа.
Файловые операции в MQL5: От базового ввода-вывода до собственного CSV-ридера Файловые операции в MQL5: От базового ввода-вывода до собственного CSV-ридера
В статье рассматриваются основные методы обработки файлов MQL5, ведение журналов торговли, обработка CSV-файлов и интеграция внешних данных. Статья содержит как теорию, так и практическое руководство по реализации. Читатели научатся шаг за шагом создавать собственный класс импортера CSV, получив практические навыки для реальных приложений.
От начального до среднего уровня: Шаблон и Typename (I) От начального до среднего уровня: Шаблон и Typename (I)
В этой статье мы начнем рассматривать одну из концепций, которую многие новички избегают. Это связано с тем, что шаблоны - непростая тема, поскольку многие не понимают основного принципа, лежащего в основе шаблона: перегрузка функций и процедур.
Прогнозирование в трейдинге и Grey-модели Прогнозирование в трейдинге и Grey-модели
В этой статье рассматривается применение Grey-моделей для прогнозирования финансовых временных рядов. Мы рассмотрим принципы работы Grey-моделей и особенности их применения к финансовым рядам. Обсудим преимущества и ограничения использования этих моделей в трейдинге.