Español Português
preview
Моделирование рынка: Position View (VII)

Моделирование рынка: Position View (VII)

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

Введение

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

В предыдущей статье, Моделирование рынка: Position View (VI), мы продемонстрировали, как сделать так, чтобы советник сообщал индикатору позиции, следует ли ему обновляться, либо, в большинстве случаев, сообщал индикатору позиции, должен ли он оставаться на графике или же его следует удалить. Хотя то, что мы рассмотрели в предыдущей статье, довольно интересно с точки зрения того, как можно начать разрабатывать некоторые компоненты в MetaTrader 5 в MetaTrader 5, это всё ещё не является чем-то действительно практичным. Это связано с тем, что мы не можем напрямую работать с индикатором позиции, чтобы указать, что ценовая линия, будь то линия стоп-лосса или тейк-профита, должна сдвинуться или, по крайней мере, удалена с позиции.

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

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

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

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


Закрытие позиции посредством взаимодействия с индикатором

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

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

Следует отметить, что, хотя на первый взгляд это может показаться сложным, реализовать это относительно просто, но только если вы будете следовать этой серии статей о разработке системы репликации/моделирования. Причина в том, что мы уже делали нечто очень похожее раньше, при создании индикатора Chart Trade. То, что мы сделаем сейчас — это механизм, очень похожий на механизм индикатора 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.             evTicTac,                        //Event of tic-tac
31.             evHideMouse,                     //Hide mouse price line
32.             evShowMouse,                     //Show mouse price line
33.             evHideBarTime,                   //Hide bar time
34.             evShowBarTime,                   //Show bar time
35.             evHideDailyVar,                  //Hide daily variation
36.             evShowDailyVar,                  //Show daily variation
37.             evHidePriceVar,                  //Hide instantaneous variation
38.             evShowPriceVar,                  //Show instantaneous variation
39.             evCtrlReplayInit,                //Initialize replay control
40.             evChartTradeBuy,                 //Market buy event
41.             evChartTradeSell,                //Market sales event 
42.             evChartTradeCloseAll,            //Event to close positions
43.             evChartTrade_At_EA,              //Event to communication
44.             evEA_At_ChartTrade,              //Event to communication
45.             evChatWriteSocket,               //Event to Mini Chat
46.             evChatReadSocket,                //Event To Mini Chat
47.             evUpdate_Position,               //Event to communication
48.             evMsgClosePositionEA             //Event to communication
49.                         };
50. //+------------------------------------------------------------------+
51. enum EnumPriority {                          //Priority list on objects
52.             ePriorityNull = -1,
53.             ePriorityDefault = 0,
54.             ePriorityChartTrade = 5,
55.             ePriorityOrders = 10
56.                         };
57. //+------------------------------------------------------------------+

Defines.mqh

Новое событие разместилось в строке 48. Думаю, совершенно очевидно, что именно будет сделано. Но почему бы не использовать событие индикатора Chart Trade, которое объявлено в строке 42? Причина проста: я не хочу ограничивать индикатор позиции или советника одним типом счета. Если бы мы были уверены, что приложение будет работать только со счетами типа NETTING, мы могли бы использовать событие в строке 42 без каких-либо проблем. Однако, если использовать те же самые приложения, то есть индикатор позиции вместе с советником, на счете типа HEDGING, при попытке закрыть позицию все позиции будут закрыты одновременно. Это одно из различий в реализации этих двух индикаторов.

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.     protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; }
011. //+------------------------------------------------------------------+
012.         bool ClosePosition(const ulong ticket)
013.             {
014.                 bool     IsBuy;
015.                 string szContract;
016.                 
017.                 if    (!PositionSelectByTicket(ticket)) return false;
018.                 IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
019.                 szContract = PositionGetString(POSITION_SYMBOL);
020.                 ZeroMemory(m_Base.TradeRequest);
021.                 m_Base.TradeRequest.action     = TRADE_ACTION_DEAL;
022.                 m_Base.TradeRequest.type       = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
023.                 m_Base.TradeRequest.price      = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS));
024.                 m_Base.TradeRequest.position   = ticket;
025.                 m_Base.TradeRequest.symbol     = szContract;
026.                 m_Base.TradeRequest.volume     = PositionGetDouble(POSITION_VOLUME);
027.                 m_Base.TradeRequest.deviation  = 1000;
028.                 
029.                 return SendToPhysicalServer() != 0;
030.             };
031. //+------------------------------------------------------------------+    
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stBase
035.         {
036.             MqlTradeRequest TradeRequest;
037.             ulong           MagicNumber;
038.             bool            bTrash;
039.         }m_Base;
040. //+------------------------------------------------------------------+
041.         struct stChartTrade
042.         {
043.             struct stEvent
044.             {
045.                 EnumEvents  ev;
046.                 string      szSymbol,
047.                             szContract;
048.                 bool        IsDayTrade;
049.                 ushort      Leverange;
050.                 double      PointsTake,
051.                             PointsStop;
052.             }Data;
053. //---
054.             bool Decode(const EnumEvents ev, const string sparam)
055.                 {
056.                     string Res[];
057.         
058.                     if (StringSplit(sparam, '?', Res) != 7) return false;
059.                     stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
060.                     if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
061.                     else return false;
062.                     
063.                     return true;
064.                 }
065. //---
066.         }m_ChartTrade;
067. //+------------------------------------------------------------------+
068.         ulong SendToPhysicalServer(void)
069.             {
070.                 MqlTradeCheckResult TradeCheck;
071.                 MqlTradeResult      TradeResult;
072.                 
073.                 ZeroMemory(TradeCheck);
074.                 ZeroMemory(TradeResult);
075.                 if (!OrderCheck(m_Base.TradeRequest, TradeCheck))
076.                 {
077.                     PrintFormat("Order System - Check Error: %d", GetLastError());
078.                     return 0;
079.                 }
080.                 m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult);
081.                 if (TradeResult.retcode != TRADE_RETCODE_DONE)
082.                 {
083.                     PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
084.                     return 0;
085.                 };
086.                 
087.                 return TradeResult.order;
088.             }
089. //+------------------------------------------------------------------+    
090.         ulong ToMarket(const ENUM_ORDER_TYPE type)
091.             {
092.                 double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
093.                 double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
094.                 uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
095.                 
096.                 ZeroMemory(m_Base.TradeRequest);
097.                 m_Base.TradeRequest.magic         = m_Base.MagicNumber;
098.                 m_Base.TradeRequest.symbol        = m_ChartTrade.Data.szContract;
099.                 m_Base.TradeRequest.price         = NormalizeDouble(price, nDigit);
100.                 m_Base.TradeRequest.action        = TRADE_ACTION_DEAL;
101.                 m_Base.TradeRequest.sl            = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
102.                 m_Base.TradeRequest.tp            = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
103.                 m_Base.TradeRequest.volume        = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
104.                 m_Base.TradeRequest.type          = type;
105.                 m_Base.TradeRequest.type_time     = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
106.                 m_Base.TradeRequest.stoplimit     = 0;
107.                 m_Base.TradeRequest.expiration    = 0;
108.                 m_Base.TradeRequest.type_filling  = ORDER_FILLING_RETURN;
109.                 m_Base.TradeRequest.deviation     = 1000;
110.                 m_Base.TradeRequest.comment       = "Order Generated by Experts Advisor.";
111. 
112.                 MqlTradeRequest TradeRequest[1];
113. 
114.                 TradeRequest[0] = m_Base.TradeRequest;
115.                 ArrayPrint(TradeRequest);
116. 
117.                 return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
118.             };
119. //+------------------------------------------------------------------+
120.         void CloseAllsPosition(void)
121.             {
122.                 for (int count = PositionsTotal() - 1; count >= 0; count--)
123.                 {
124.                     if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue;
125.                     if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue;
126.                     ClosePosition(PositionGetInteger(POSITION_TICKET));
127.                 }
128.             };
129. //+------------------------------------------------------------------+    
130.     public    :
131. //+------------------------------------------------------------------+
132.         C_Orders(const ulong magic)
133.             {
134.                 m_Base.MagicNumber = magic;
135.             }
136. //+------------------------------------------------------------------+    
137.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
138.             {
139.                 switch (id)
140.                 {
141.                     case CHARTEVENT_CUSTOM + evChartTradeBuy     :
142.                     case CHARTEVENT_CUSTOM + evChartTradeSell    :
143.                     case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
144.                         if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
145.                         {
146.                             case evChartTradeBuy:
147.                                 ToMarket(ORDER_TYPE_BUY);
148.                                 break;
149.                             case evChartTradeSell:
150.                                 ToMarket(ORDER_TYPE_SELL);
151.                                 break;
152.                             case evChartTradeCloseAll:
153.                                 CloseAllsPosition();
154.                                 break;
155.                         }
156.                         break;
157.                     case CHARTEVENT_CUSTOM + evMsgClosePositionEA:
158.                         ClosePosition((ulong)StringToInteger(sparam));
159.                         break;
160.                 }
161.             }
162. //+------------------------------------------------------------------+    
163. };
164. //+------------------------------------------------------------------+

C_Orders.mqh

Хотя мы показываем полный код класса C_Orders, чтобы вы могли понять происходящее в случае, если вы не следите за этой последовательностью, мы добавили всего три строки в код класса. Это строки 157, 158 и 159. И это всё, что нужно было добавить в код советника, чтобы он мог обрабатывать запрос на закрытие позиции. Обратите внимание: мы здесь вообще не фильтруем никакие данные. Поэтому это же самое событие может быть сгенерировано, например, из сервиса или даже из скрипта. Мы никак не ограничиваем то, кто именно отдает команду советнику на выполнение действия, которое в данном случае заключается в закрытии позиции. Единственное, о чем нам действительно нужно беспокоиться, это указать в поле sparam идентификатор позиции, которую мы хотим закрыть. Если тикет существует, позиция будет закрыта.

Теперь обратите внимание на момент, который я упомянул ранее: мы не могли бы использовать событие evChartTradeCloseAll, потому что вызов, который должен быть выполнен, отличается от вызова, используемого в evChartTradeCloseAll. Причина заключается в том, что данная реализация предназначена для работы с любым типом счета, будь то NETTING или HEDGING. Хорошо. Часть, относящаяся к советнику, уже реализована и, безусловно, работает, потому что этот код, который мы используем в классе C_Orders, тестировался уже довольно давно.

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

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #define def_SufixTake        "Take"
005. #define def_SufixStop        "Stop"
006. //+------------------------------------------------------------------+
007. #define def_NameBtnClose     m_Infos.szShortName + "Close"
008. //+------------------------------------------------------------------+
009. #define def_PathBtns "Images\\Market Replay\\Orders\\"
010. #define def_Btn_Close def_PathBtns + "Btn_Close.bmp"
011. #resource "\\" + def_Btn_Close;
012. //+------------------------------------------------------------------+
013. #include "..\Auxiliar\C_Terminal.mqh"
014. //+------------------------------------------------------------------+
015. class C_IndicatorPosition : private C_Terminal
016. {
017.     private    :
018.         struct st00
019.         {
020.             ulong     ticket;
021.             color     corPrice, corTake, corStop;
022.             string    szShortName;
023.         }m_Infos;
024. //+------------------------------------------------------------------+
025.         inline void MoveLineInfos(const string szDefine, const double price)
026.         {
027.             string szName = m_Infos.szShortName + szDefine;
028.             int x, y;
029.             
030.             if (price <= 0) ObjectDelete(0, szName);
031.             else
032.             {
033.                 if (ObjectFind(0, szName) < 0)
034.                 {
035.                     if (szDefine == def_SufixTake) CreateLineInfos(def_SufixTake, m_Infos.corTake, "Take Profit point."); else
036.                     if (szDefine == def_SufixStop) CreateLineInfos(def_SufixStop, m_Infos.corStop, "Stop Loss point."); else
037.                     {
038.                         CreateLineInfos(NULL, m_Infos.corPrice, "Position opening price.");
039.                         CreateObjectGraphics(def_NameBtnClose, OBJ_BITMAP_LABEL, clrNONE, (EnumPriority)(ePriorityOrders + 1));
040.                         ObjectSetString(0, def_NameBtnClose, OBJPROP_BMPFILE, 0, "::" + def_Btn_Close);
041.                     }
042.                 }                    
043.                 ObjectSetDouble(0, szName, OBJPROP_PRICE, price);
044.                 if (szDefine == NULL)
045.                 {
046.                     ChartTimePriceToXY(0, 0, 0, price, x, y);
047.                     ObjectSetInteger(0, def_NameBtnClose, OBJPROP_XDISTANCE, 130);
048.                     ObjectSetInteger(0, def_NameBtnClose, OBJPROP_YDISTANCE, y - 8);
049.                 }
050.             }
051.         }
052. //+------------------------------------------------------------------+
053.         void CreateLineInfos(string szObjName, const color cor, const string szDescription = "\n")
054.         {            
055.             szObjName = m_Infos.szShortName + szObjName;
056.             CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop))));
057.             ObjectSetString(0, szObjName, OBJPROP_TEXT, szDescription);
058.             ObjectSetString(0, szObjName, OBJPROP_TOOLTIP, szDescription);
059.             ObjectSetInteger(0, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice);
060.         }
061. //+------------------------------------------------------------------+
062.     public    :
063. //+------------------------------------------------------------------+
064.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
065.             :C_Terminal()
066.         {
067.             ZeroMemory(m_Infos);
068.             m_Infos.corPrice = corPrice;
069.             m_Infos.corTake  = corTake;
070.             m_Infos.corStop  = corStop;
071.         }
072. //+------------------------------------------------------------------+
073.         ~C_IndicatorPosition()
074.         {
075.             if (m_Infos.ticket != 0)
076.                 ObjectsDeleteAll(0, IntegerToString(m_Infos.ticket));
077.         }
078. //+------------------------------------------------------------------+
079.         bool CheckCatch(ulong ticket)
080.         {
081.             m_Infos.szShortName = IntegerToString(m_Infos.ticket = ticket);            
082.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
083.             if (ObjectFind(0, m_Infos.szShortName) >= 0)
084.             {
085.                 m_Infos.ticket = 0;
086.                 return false;
087.             }
088.             IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
089.             EventChartCustom(0, evUpdate_Position, ticket, 0, "");
090.             
091.             return true;
092.         }
093. //+------------------------------------------------------------------+
094.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
095.         {
096.             static double price = 0;
097.             
098.             switch (id)
099.             {
100.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
101.                     if (lparam != m_Infos.ticket) return;
102.                     if (!PositionSelectByTicket(m_Infos.ticket))
103.                     {
104.                         ChartIndicatorDelete(0, 0, m_Infos.szShortName);
105.                         return;
106.                     };
107.                     MoveLineInfos(NULL, price = PositionGetDouble(POSITION_PRICE_OPEN));
108.                     MoveLineInfos(def_SufixTake, PositionGetDouble(POSITION_TP));
109.                     MoveLineInfos(def_SufixStop, PositionGetDouble(POSITION_SL));
110.                     break;
111.                 case CHARTEVENT_CHART_CHANGE:
112.                     MoveLineInfos(NULL, price);
113.                     break;
114.                 case CHARTEVENT_OBJECT_CLICK:
115.                     if (sparam == def_NameBtnClose)
116.                         EventChartCustom(0, evMsgClosePositionEA, 0, 0, m_Infos.szShortName);
117.                     break;
118.             }
119.             ChartRedraw();
120.         }
121. //+------------------------------------------------------------------+
122. };
123. //+------------------------------------------------------------------+
124. #undef def_Btn_Close
125. #undef def_PathBtns
126. //+------------------------------------------------------------------+
127. #undef def_NameBtnClose
128. //+------------------------------------------------------------------+
129. #undef def_SufixTake
130. #undef def_SufixStop
131. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

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

Обратите внимание, что в строке 07 приведено определение. Его цель — определить имя объекта, с которым мы будем взаимодействовать для закрытия позиции. Затем, между строками 09 и 11 у нас есть то, что нужно будет подготовить локально. Это потребуется, если вы захотите создать собственный шаблон. Если вы не хотите этим заниматься, не волнуйтесь, мы скоро разместим приложения в статье, как это было сделано в предыдущей статье.

Хорошо, давайте продолжим. В строке 09 мы указываем местоположение изображения bitmap. В строке 10 мы указываем, что представляет собой изображение bitmap. В этом случае вам потребуется подготовить изображение размером 16 x 16 пикселей. Но можно использовать другое изображение, если вы внесете соответствующие изменения в код. Позже мы увидим, в каком именно месте кода вам нужно будет внести это изменение. Давайте пока сосредоточимся на строке 11. В этой строке мы указываем компилятору MQL5, что хотим включить bitmap в качестве ресурса в исполняемый файл. Это позволит избежать необходимости прикреплять bitmap к остальной части приложения после компиляции, что значительно упрощает задачу как для нас, так и для конечного пользователя.

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

Теперь обратите внимание, потому что понимание того, что происходит в процедуре, определенной в строке 94, будет очень важно для того, что мы увидим дальше. Будьте внимательны, в строке 96 была добавлена статическая переменная. Она со временем перестанет существовать. Но на данный момент это позволяет нам локально сохранять цену, по которой была открыта позиция, и хранить её в памяти. Прошу заметить, что эта же переменная инициализируется в строке 107. Хорошо. Теперь у нас есть цена, по которой была открыта позиция. Важно отметить, что это значение будет оставаться постоянным в течение всего этого времени. Но нам это нужно в очень специфический момент. И этот момент находится в строке 112. Обратите внимание, что данная строка находится внутри обработчика события CHARTEVENT_CHART_CHANGE. Причина проста. Когда вы меняете ценовой масштаб на графике, ценовые линии меняются вместе с масштабом.

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

Однако в процедуре DispatchMessage нас на самом деле интересует следующий блок, то есть, код, реализованный в строке 114. Обратите внимание, что там мы проверяем, был ли выполнен щелчок по объекту. Когда MetaTrader 5 сообщит о том, что это произошло, он укажет, какой именно объект получил клик. И имя объекта поступает в переменную sparam. Таким образом, в строке 115 мы проверяем, совпадает ли имя с именем объекта, созданного в самом начале, то есть в строке 07. Если проверка, выполненная в строке 115, оказывается истинной, в строке 116 мы запрашиваем у MetaTrader 5 отправку пользовательского события для его последующего перехвата советником.

Проанализируйте значение этого события. Это именно те значения, которые советник ожидает для интерпретации запроса на закрытие позиции. Интересно, не правда ли? Теперь давайте посмотрим, как на графике создается и позиционируется объект (или bitmap), представляющий кнопку закрытия позиции. Для этого в данный момент мы сосредоточим наше внимание на строке 25, где выполняется ключевая часть.

Обратите внимание, что здесь мы внесли несколько изменений по сравнению с предыдущей версией. Первым шагом были добавлены переменные x и y в строке 28. После этого, в строке 38, мы создали строку с ценой, как это было сделано ранее. Однако, поскольку нам нужен ещё один объект, которым в данном случае будет кнопка для закрытия позиции, мы создаем его в строке 39. Важно отметить то, как мы это делаем и какие объекты создаём. То есть, объект OBJ_BITMAP_LABEL. Для корректного позиционирования этому объекту требуются значения координат X и Y. Мы не можем использовать ценовые координаты. Сразу после создания объекта, в строке 40, мы указываем MetaTrader 5 использовать изображение, которое включено в качестве ресурса в исполняемый файл. Именно в этой точке, а не в строке 11 мы привязываем изображение, включенное в исполняемый файл, и перестаем использовать изображение из каталога. Но до этого момента мы только создали объект; мы всё ещё не отобразили его на экране.

Чтобы расположить его в правильной точке, нам нужно преобразовать координаты цена-время в декартовы координаты. К счастью, в MQL5 есть вспомогательная функция, которая используется в строке 46. Там мы преобразуем цену в подходящую координату оси Y. Это самая простая часть, но следует принять небольшие меры предосторожности. Чтобы понять это, посмотрите, что мы делаем в строке 48. Там я вычитаю восемь из значения, возвращаемого функцией в строке 46. Зачем я выполняю это вычитание и почему я использую значение восемь? Там мы делаем это вычитание, потому что объект OBJ_BITMAP_LABEL привязан не к центру, а к левому верхнему углу.

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


Дальнейшее улучшение индикатора

Если вы остановитесь и немного подумаете, то заметите, что у нас уже есть объект, который позволяет закрывать позицию. Ценовые линии уже показаны на графике. У нас есть возможность отправлять запросы от индикатора позиции к советнику. Всё это с минимальными необходимыми изменениями. Но как бы мы могли применить тот же тип взаимодействия, используемый для закрытия позиции к линиям Stop Loss и Take Profit, чтобы удалить ограничение убытков и ограничение прибыли? И делать это напрямую через взаимодействие с индикатором позиции, не используя торговую вкладку терминала MetaTrader 5.

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

Но что, если сделать это немного более удобным способом? Или, если быть точнее, сделать так, чтобы вам вообще не пришлось об этом беспокоиться. Достаточно ли просто указать, что линия представляет определенный элемент, и код сам позаботится о ее правильном создании? Со всеми уже включенными и функциональными объектами. Было бы здорово, не так ли? Хорошо. Это то, что мы сделаем прямо сейчас. Мы создадим механизм для автоматизации. Но это задача, требующая внимания. И я хочу, чтобы вы понимали, что я делаю и почему я это делаю. Мы разберем это шаг за шагом. Таким образом, вы сможете легко модифицировать код, если захотите.

Первое, что мы сделаем — добавим несколько новых событий в файл 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.             evTicTac,                        //Event of tic-tac
31.             evHideMouse,                     //Hide mouse price line
32.             evShowMouse,                     //Show mouse price line
33.             evHideBarTime,                   //Hide bar time
34.             evShowBarTime,                   //Show bar time
35.             evHideDailyVar,                  //Hide daily variation
36.             evShowDailyVar,                  //Show daily variation
37.             evHidePriceVar,                  //Hide instantaneous variation
38.             evShowPriceVar,                  //Show instantaneous variation
39.             evCtrlReplayInit,                //Initialize replay control
40.             evChartTradeBuy,                 //Market buy event
41.             evChartTradeSell,                //Market sales event 
42.             evChartTradeCloseAll,            //Event to close positions
43.             evChartTrade_At_EA,              //Event to communication
44.             evEA_At_ChartTrade,              //Event to communication
45.             evChatWriteSocket,               //Event to Mini Chat
46.             evChatReadSocket,                //Event To Mini Chat
47.             evUpdate_Position,               //Event to communication
48.             evMsgClosePositionEA,            //Event to communication
49.             evMsgCloseTakeProfit,            //Event to communication
50.             evMsgCloseStopLoss               //Event to communication
51.                         };
52. //+------------------------------------------------------------------+
53. enum EnumPriority {                          //Priority list on objects
54.             ePriorityNull = -1,
55.             ePriorityDefault = 0,
56.             ePriorityChartTrade = 5,
57.             ePriorityOrders = 10
58.                         };
59. //+------------------------------------------------------------------+

Defines.mqh

Обратите внимание, что всё очень просто и практично. Нетрудно понять, что здесь было сделано. После этого мы перейдем к следующему этапу. На этом первом этапе работа оказывается немного более трудоемкой. Мы будем продвигаться без спешки, чтобы вы действительно поняли, что происходит. Хорошо, мы хотим добавить механизм, который позволит нам удалить линии Take Profit и Stop Loss. Но недостаточно просто добавить это в блок сообщений, так как мы не можем выполнить запрос. Поэтому нам нужно изменить код советника. Чтобы сделать это изменение, мы перейдем непосредственно к ключевому моменту — заголовочному файлу C_Orders.mqh. В этот файл будет добавлен новый код, как показано ниже.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Defines.mqh"
005. //+------------------------------------------------------------------+
006. class C_Orders
007. {
008.     protected:
009. //+------------------------------------------------------------------+
010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; }
011. //+------------------------------------------------------------------+
012.         bool ClosePosition(const ulong ticket)
013.             {
014.                 bool   IsBuy;
015.                 string szContract;
016.                 
017.                 if    (!PositionSelectByTicket(ticket)) return false;
018.                 IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
019.                 szContract = PositionGetString(POSITION_SYMBOL);
020.                 ZeroMemory(m_Base.TradeRequest);
021.                 m_Base.TradeRequest.action    = TRADE_ACTION_DEAL;
022.                 m_Base.TradeRequest.type      = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
023.                 m_Base.TradeRequest.price     = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS));
024.                 m_Base.TradeRequest.position  = ticket;
025.                 m_Base.TradeRequest.symbol    = szContract;
026.                 m_Base.TradeRequest.volume    = PositionGetDouble(POSITION_VOLUME);
027.                 m_Base.TradeRequest.deviation = 1000;
028.                 
029.                 return SendToPhysicalServer() != 0;
030.             };
031. //+------------------------------------------------------------------+    
032.     private    :
033. //+------------------------------------------------------------------+
034.         struct stBase
035.         {
036.             MqlTradeRequest TradeRequest;
037.             ulong           MagicNumber;
038.             bool            bTrash;
039.         }m_Base;
040. //+------------------------------------------------------------------+
041.         struct stChartTrade
042.         {
043.             struct stEvent
044.             {
045.                 EnumEvents  ev;
046.                 string      szSymbol,
047.                             szContract;
048.                 bool        IsDayTrade;
049.                 ushort      Leverange;
050.                 double      PointsTake,
051.                             PointsStop;
052.             }Data;
053. //---
054.             bool Decode(const EnumEvents ev, const string sparam)
055.                 {
056.                     string Res[];
057.         
058.                     if (StringSplit(sparam, '?', Res) != 7) return false;
059.                     stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])};
060.                     if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc;
061.                     else return false;
062.                     
063.                     return true;
064.                 }
065. //---
066.         }m_ChartTrade;
067. //+------------------------------------------------------------------+
068.         ulong SendToPhysicalServer(void)
069.             {
070.                 MqlTradeCheckResult  TradeCheck;
071.                 MqlTradeResult       TradeResult;
072.                 
073.                 ZeroMemory(TradeCheck);
074.                 ZeroMemory(TradeResult);
075.                 if (!OrderCheck(m_Base.TradeRequest, TradeCheck))
076.                 {
077.                     PrintFormat("Order System - Check Error: %d", GetLastError());
078.                     return 0;
079.                 }
080.                 m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult);
081.                 if (TradeResult.retcode != TRADE_RETCODE_DONE)
082.                 {
083.                     PrintFormat("Order System - Send Error: %d", TradeResult.retcode);
084.                     return 0;
085.                 };
086.                 
087.                 return TradeResult.order;
088.             }
089. //+------------------------------------------------------------------+    
090.         ulong ToMarket(const ENUM_ORDER_TYPE type)
091.             {
092.                 double price  = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID));
093.                 double vol    = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP);
094.                 uchar  nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS);
095.                 
096.                 ZeroMemory(m_Base.TradeRequest);
097.                 m_Base.TradeRequest.magic        = m_Base.MagicNumber;
098.                 m_Base.TradeRequest.symbol       = m_ChartTrade.Data.szContract;
099.                 m_Base.TradeRequest.price        = NormalizeDouble(price, nDigit);
100.                 m_Base.TradeRequest.action       = TRADE_ACTION_DEAL;
101.                 m_Base.TradeRequest.sl           = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit);
102.                 m_Base.TradeRequest.tp           = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit);
103.                 m_Base.TradeRequest.volume       = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit);
104.                 m_Base.TradeRequest.type         = type;
105.                 m_Base.TradeRequest.type_time    = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
106.                 m_Base.TradeRequest.stoplimit    = 0;
107.                 m_Base.TradeRequest.expiration   = 0;
108.                 m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN;
109.                 m_Base.TradeRequest.deviation    = 1000;
110.                 m_Base.TradeRequest.comment      = "Order Generated by Experts Advisor.";
111. 
112.                 MqlTradeRequest TradeRequest[1];
113. 
114.                 TradeRequest[0] = m_Base.TradeRequest;
115.                 ArrayPrint(TradeRequest);
116. 
117.                 return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0);
118.             };
119. //+------------------------------------------------------------------+
120.         void CloseAllsPosition(void)
121.             {
122.                 for (int count = PositionsTotal() - 1; count >= 0; count--)
123.                 {
124.                     if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue;
125.                     if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue;
126.                     ClosePosition(PositionGetInteger(POSITION_TICKET));
127.                 }
128.             };
129. //+------------------------------------------------------------------+    
130.         void ModifyValueSLTP(const ulong ticket, const string symbol, const double sl, const double tp)
131.             {
132.                 ZeroMemory(m_Base.TradeRequest);            
133.                 MqlTradeRequest TradeRequest[1];
134. 
135.                 m_Base.TradeRequest.magic    = m_Base.MagicNumber;
136.                 m_Base.TradeRequest.action   = TRADE_ACTION_SLTP;
137.                 m_Base.TradeRequest.symbol   = symbol;
138.                 m_Base.TradeRequest.position = ticket;
139.                 m_Base.TradeRequest.sl       = sl;
140.                 m_Base.TradeRequest.tp       = tp;
141.                 
142.                 TradeRequest[0] = m_Base.TradeRequest;
143.                 ArrayPrint(TradeRequest);
144.                 
145.                 SendToPhysicalServer();
146.             }
147. //+------------------------------------------------------------------+    
148.     public    :
149. //+------------------------------------------------------------------+
150.         C_Orders(const ulong magic)
151.             {
152.                 m_Base.MagicNumber = magic;
153.             }
154. //+------------------------------------------------------------------+    
155.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
156.             {
157.                 switch (id)
158.                 {
159.                     case CHARTEVENT_CUSTOM + evChartTradeBuy     :
160.                     case CHARTEVENT_CUSTOM + evChartTradeSell    :
161.                     case CHARTEVENT_CUSTOM + evChartTradeCloseAll:
162.                         if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev)
163.                         {
164.                             case evChartTradeBuy:
165.                                 ToMarket(ORDER_TYPE_BUY);
166.                                 break;
167.                             case evChartTradeSell:
168.                                 ToMarket(ORDER_TYPE_SELL);
169.                                 break;
170.                             case evChartTradeCloseAll:
171.                                 CloseAllsPosition();
172.                                 break;
173.                         }
174.                         break;
175.                     case CHARTEVENT_CUSTOM + evMsgClosePositionEA:
176.                         ClosePosition((ulong)(lparam));
177.                         break;
178.                     case CHARTEVENT_CUSTOM + evMsgCloseTakeProfit:
179.                         ModifyValueSLTP((ulong)(lparam), sparam, dparam, 0);
180.                         break;
181.                     case CHARTEVENT_CUSTOM + evMsgCloseStopLoss:
182.                         ModifyValueSLTP((ulong)(lparam), sparam, 0, dparam);
183.                         break;
184.                 }
185.             }
186. //+------------------------------------------------------------------+    
187. };
188. //+------------------------------------------------------------------+

C_Orders.mqh

Приведённый выше код является полным, как и все остальные. Глядя на это, можно подумать: "ну, я не вижу большой разницы по сравнению с предыдущей версией". На самом деле, я всегда стараюсь избегать очень радикальных изменений в коде. Однако здесь произошли изменения. Эти изменения призваны обеспечить поддержку, необходимую нам на данном и на следующем этапе развития. Но давайте сначала сосредоточимся на этом шаге, который заключается в удалении линий тейк-профита и стоп-лосса. Обратите внимание, что в строке 130 приведенного выше кода у нас появилась новая процедура. Данная процедура служит для изменения значения Stop Loss и Take Profit открытой позиции. Обратите внимание на это. В настоящий момент мы работаем не с ордерами, находящимися в книге заявок, а с уже открытой позицией. Разница между обеими ситуациями существует.

По сути, мы получаем некоторые параметры и отправляем на сервер запрос на изменение уровней Take Profit и Stop Loss. В строке 143 мы выводим на терминал запрос, отправленный на сервер. Это позволяет нам проверить ее и проанализировать работу системы. Хорошо, поскольку данная процедура довольно проста и в неё нужно только передать аргументы, мы можем перейти к следующему пункту, в котором был изменен код. Данный момент находится в процедуре, описанной в строке 155.

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

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

В обоих случаях мы имеем одну и ту же цель, то есть процедуру ModifyValueSLTP, которая служит для изменения уровней Stop Loss и Take Profit позиции. Обратите внимание на то, что именно передается в каждом параметре. Разница здесь заключается только в том, где используется параметр dparam. В одном случае это относится к тейк-профиту, а в другом — к стоп-лоссу. Таким образом, мы можем удалить один из уровней, так как, когда один из них получит нулевое значение, он будет удален. Это происходит, потому что торговый сервер понимает, что нулевое значение отображает, что лимит, будь то тейк-профит или стоп-лосс, просто не существует. На этом мы завершаем необходимые изменения в коде советника, без необходимости вносить какие-либо другие правки. По крайней мере на данный момент.


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

В этой статье я показал, как можно довольно простым и эффективным способом реализовать закрытие позиции с помощью взаимодействия индикатора Mouse Study и индикатора позиции в связке с соответствующим образом запрограммированным советником. Вся реализация была выполнена довольно элегантным способом, так как не потребовалось вносить серьезных изменений в код. Я также показал всю часть, которую необходимо было реализовать в коде советника, чтобы мы могли создать механизм, предназначенный для удаления линий Take Profit и Stop Loss.

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

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

Файл Описание
Experts\Expert Advisor.mq5
Показывает на графике взаимодействие между Chart Trade и советником. (Для взаимодействия необходимо Mouse Study)
Indicators\Chart Trade.mq5 Создает окно для настройки ордера, который будет отправлен. (Для взаимодействия необходимо Mouse Study)
Indicators\Market Replay.mq5 Создает элементы управления для взаимодействия с сервисом репликации/моделирования. (Для взаимодействия необходимо Mouse Study)
Indicators\Mouse Study.mq5 Обеспечивает взаимодействие между графическими элементами управления и пользователем (необходимое как для работы системы репликации/моделирования, так и на реальном рынке).
Indicators\Order Indicator.mq5 Отвечает за формирование рыночных ордеров, обеспечение взаимодействия с ними и контроль над ними.
Indicators\Position View.mq5 Отвечает за отображение рыночных позиций, обеспечивая и их взаимодействие и контроль.
Services\Market Replay.mq5 Создает и поддерживает сервис репликации/моделирования рынка (главный файл всей системы).

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

Прикрепленные файлы |
Anexo.zip (779.24 KB)
Создание пользовательских индикаторов в MQL5 (Часть 1): Построение трендового индикатора на основе пивотов с градиентной заливкой на Canvas Создание пользовательских индикаторов в MQL5 (Часть 1): Построение трендового индикатора на основе пивотов с градиентной заливкой на Canvas
В этой статье мы создадим трендовый индикатор на основе пивотов в MQL5. Он рассчитывает быстрые и медленные пивотные линии за заданные пользователем периоды, определяет направление тренда по положению цены относительно этих линий и отмечает начало тренда стрелками, при необходимости продлевая линии за пределы текущего бара. Индикатор поддерживает динамическое отображение отдельных восходящих и нисходящих линий настраиваемых цветов, пунктирных быстрых линий, которые меняют цвет при смене тренда, а также дополнительную градиентную заливку между линиями с использованием объекта класса CCanvas для более наглядного выделения трендовой области.
От начального до среднего уровня: Навигация по песочнице От начального до среднего уровня: Навигация по песочнице
В данной статье мы рассмотрим два способа наблюдения за содержимым песочницы и даже взаимодействия с ним, используя MetaTrader 5 в качестве базовой платформы. Понимание содержания этой статьи имеет основополагающее значение для понимания того, что будет рассмотрено в последующих статьях.
Реализация модели гауссовской смеси в MQL5 Реализация модели гауссовской смеси в MQL5
В статье рассматриваются теоретические основы и практическая реализация модели смеси нормальных распределений (Gaussian Mixture Model) на языке MQL5. Представлен класс CGMM и примеры его использования, а также методология выбора оптимального количества компонентов модели с помощью информационных критериев AIC/BIC. Значительное внимание уделяется принципу работы EM-алгоритма — признанного стандарта при обучении вероятностных моделей со скрытыми(латентными) переменными. Реализована поддержка режима обучения MAP и инициализация параметров с помощью K-Means++. Готовый инструмент для кластеризации, оценки многомерной плотности и генерации данных теперь доступен и в MetaTrader 5.
Встраивание торговой дисциплины в код (Часть 2): Разработка механизма принудительного соблюдения дневного лимита сделок на уровне всего счета в MQL5 Встраивание торговой дисциплины в код (Часть 2): Разработка механизма принудительного соблюдения дневного лимита сделок на уровне всего счета в MQL5
Мы разработали систему, которая устанавливает ежедневный лимит на количество сделок, чтобы вы соблюдали свои торговые правила. Она отслеживает все совершенные сделки на счете и автоматически вмешивается при достижении установленного лимита, предотвращая дальнейшую активность. Благодаря интеграции контроля непосредственно в платформу, система обеспечивает поддержание дисциплины даже при повышении рыночного давления.