Português
preview
Simulación de mercado: Position View (VII)

Simulación de mercado: Position View (VII)

MetaTrader 5Probador |
93 0
Daniel Jose
Daniel Jose

Introducción

Hola a todos, y sean bienvenidos a otro artículo de la serie sobre cómo construir un sistema de repetición/simulación.

En el artículo anterior, Simulación de mercado: Position View (VI), demostré cómo hacer que el Asesor Experto le indicara al indicador de posición si debía actualizarse o no, o, en la mayoría de los casos, indicarle al indicador de posición que debía permanecer en el gráfico o retirarse de él. Aunque lo que se vio en el artículo anterior es algo bastante interesante, desde el punto de vista de cómo puedes empezar a diseñar algunos componentes en MetaTrader 5, todavía no es algo realmente práctico. Esto se debe a que no podemos trabajar directamente en el indicador de posición, para indicar que una línea de precio, ya sea la del Stop Loss o la del Take Profit, debe moverse o, como mínimo, retirarse de la posición.

Aun así, todavía tenemos otra cuestión, y quizá sea aún más interesante abordarla de inmediato: la posibilidad de cerrar una posición mediante alguna forma de interacción con el indicador de posición. Esto nos ahorraría el trabajo o la preocupación de tener que mantener la ventana del terminal en la pestaña de trading, algo que puede resultar útil durante las pruebas, pero que se vuelve innecesario si podemos interactuar con el indicador de posición.

En este artículo, comenzaremos a realizar algunas mejoras en el indicador de posición, para poder interactuar con él y modificar las líneas de precio, o cerrar una posición directamente mediante la interacción con el indicador de posición. Antes de entrar realmente en la implementación, conviene aclarar algo, sobre todo para quienes no estén al tanto. No es posible, de ninguna manera, usar un indicador para modificar algo en el servidor de trading. Esto se debe a que MetaTrader 5 cuenta con un sistema de seguridad que permite únicamente a los Asesores Expertos actuar sobre una orden o una posición.

Aunque puedas usar el propio MetaTrader 5, en su configuración predeterminada, para tal fin, ninguna otra aplicación que no sea un Asesor Experto podrá manipular órdenes o posiciones. Así, lo que haremos a partir de este momento, es crear un mecanismo de comunicación entre el indicador de posición y un Asesor Experto, de manera que tengamos la ilusión de que el indicador de posición afecta las órdenes o posiciones registradas en el servidor. Pero esto, como acabo de decir, es solo una ilusión.

Si eres entusiasta y pretendes usar lo que explicaré dentro de poco, debes saber que tendrás que lograr que las aplicaciones en MetaTrader 5 trabajen de forma colaborativa. No pienses que una aplicación separada podrá hacer todo lo que se va a mostrar, pues esto no es algo real, sino solo una ilusión que crearemos. Esa ilusión, si se implementa bien, te permitirá trabajar u operar en MetaTrader 5 de una manera bastante agradable y cómoda. Entonces, vayamos a lo que realmente interesa.


Cierre de una posición mediante la interacción con el indicador

Antes de comenzar a implementar ciertos mecanismos, vamos a crear un mecanismo de seguridad, por así decirlo. Porque, incluso en una cuenta demo, queremos empezar desde el principio a implementar una forma de cerrar una posición sin tener que recurrir a otros mecanismos que no sean el indicador de posición.

Esto es relativamente simple y directo. Porque solo necesitamos crear un objeto en el gráfico que quedará en una posición determinada. Cuando este objeto reciba un clic o cualquier evento, obligará al indicador de posición a generar un evento. El Asesor Experto capturará ese evento. Dentro del Asesor Experto, crearemos los mecanismos necesarios para que este evento, proveniente del indicador de posición, pueda usarse para enviar una solicitud al servidor. Esta solicitud servirá para cerrar la posición que el indicador de posición muestre en el gráfico.

Observa que esto, aunque a primera vista parezca complejo y difícil, es relativamente sencillo de implementar. Siempre que estés siguiendo esta secuencia de artículos sobre cómo desarrollar un sistema de repetición/simulador. El motivo es que ya hicimos algo muy parecido antes, al construir el indicador Chart Trade. Lo que haremos ahora es un mecanismo muy parecido al del indicador Chart Trade, pero existen algunas pequeñas diferencias por las que el indicador de posición funciona, en términos de código, de forma diferente al indicador Chart Trade. Entonces, empecemos para que puedas entender de qué estoy hablando.

Muy bien, lo primero que haremos será añadir un nuevo evento al archivo Defines.mqh. El nuevo archivo se muestra a continuación:

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

El nuevo evento se colocó en la línea 48. Creo que resulta bastante evidente lo que se hará. Pero ¿por qué no usar el evento del indicador Chart Trade, que está declarado en la línea 42? El motivo es simple: no quiero limitar el indicador de posición ni el Asesor Experto a un solo tipo de cuenta. Si tuviéramos la certeza de que la aplicación solo se ejecutaría en cuentas de tipo NETTING , podríamos usar el evento de la línea 42 sin ningún problema. Sin embargo, si llegaras a usar estas mismas aplicaciones, es decir, el indicador de posición junto con el Asesor Experto, en una cuenta HEDGING, al intentar cerrar una posición, todas las posiciones se cerrarían al mismo tiempo. Esta es una de las diferencias que existen en la implementación de ambos indicadores.

Muy bien, ahora que sabes lo que haremos, necesitamos modificar el código del indicador de posición y también el código del Asesor Experto, para que el evento se interprete y ejecute correctamente. Bien, puedes estar preocupado y pensar: vaya, esto será muy laborioso. Tendremos que hacer muchos cambios en el código. Pero, si pensaste esto, es porque no comprendes realmente el código. Serán muy pocos los cambios necesarios para ejecutar esta tarea. Veamos primero los cambios que deben realizarse en el código del Asesor Experto. Estos se muestran en el siguiente código.

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

Aunque estamos mostrando el código completo de la clase C_Orders, para que puedas entender lo que ocurre en caso de que no estés siguiendo esta secuencia, solo añadimos apenas tres líneas al código de la clase. Dichas líneas son las 157, 158 y 159. Y eso es lo único que hubo que añadir al código del Asesor Experto para que pueda procesar una solicitud de cierre de posición. Ahora, presta atención a lo siguiente: aquí no estamos filtrando absolutamente ningún dato. Por eso, este mismo evento puede generarse desde un servicio, o incluso desde un script, por ejemplo. No estamos restringiendo ni limitando en nada quién le dice al Asesor Experto que ejecute una acción, que en este caso consiste en cerrar una posición. Lo único que realmente debe preocuparnos es indicar, en el campo sparam, el identificador de la posición que queremos cerrar. Si el ticket existe, la posición se cerrará.

Ahora observa el punto que mencioné antes, no podríamos usar el evento evChartTradeCloseAll, porque la llamada que debe ejecutarse es distinta de la que se usa en evChartTradeCloseAll. La razón es que esta implementación está pensada para trabajar con cualquier tipo de cuenta, ya sea NETTING o HEDGING. Bien. La parte del Asesor Experto ya fue implementada y, con toda seguridad, funciona. Porque este código que usamos en la clase C_Orders se prueba desde hace bastante tiempo.

Podemos concentrarnos en la parte que querido lector y entusiasta, quizá imagines que es la más compleja. Pero verás que es igualmente simple. Bien, para mostrar que la implementación que debe realizarse en el indicador de posición es algo simple, no cambiaré el indicador abruptamente en este momento. Simplemente añadiré lo necesario para que la interacción con el indicador de posición envíe una solicitud al Asesor Experto para cerrar la posición indicada. Esto se consigue muy fácilmente, como puedes ver a continuación.

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

Muy bien, aquí tenemos el código de la clase C_IndicatorPosition. Observa que cambió muy, pero muy poco, en términos de implementación. Sin embargo, este código puede colocar un objeto en el gráfico que nos permite interactuar y pedir que el Asesor Experto cierre la posición. Pero ¿cómo ocurre esto? Bien, analicemos este código antes de mejorarlo un poco. Porque este código aún no es adecuado para seguir desarrollando nuestro indicador. Pero conviene que, querido lector, lo entiendas antes de pasar a un código más elaborado.

Observa que, en la línea siete, tenemos una definición. Su objetivo es definir un nombre para el objeto con el que interactuaremos para cerrar la posición. Luego, entre las líneas nueve y once, tenemos algo que deberás preparar localmente. Esto será necesario si quieres crear tu propia plantilla. Si no quieres hacer este trabajo, no te preocupes, porque dentro de poco pondré las aplicaciones a disposición en el artículo, tal como se hizo en el artículo anterior.

Bien, continuemos. En la línea nueve, indicamos dónde estará ubicado el bitmap. En la línea 10, especificamos cuál es el bitmap. En este caso, deberás preparar una imagen de 16 x 16 píxeles. Pero podrás usar una imagen diferente, siempre que hagas los cambios correspondientes en el código. Más adelante veremos en qué punto del código deberás hacer el cambio. Por ahora, vamos a centrarnos en la línea 11. En esta línea, le indicamos al compilador MQL5 que queremos que el bitmap se incluya como recurso en el ejecutable. Esto permite que, una vez realizada la compilación, no sea necesario incluir el bitmap junto con el resto de la aplicación, lo que facilita mucho las cosas tanto para nosotros como para el usuario final.

Muy bien, seguimos avanzando en el código sin muchos cambios, salvo dos puntos. Por ahora, no vamos a preocuparnos por la rutina de la línea 25, que es uno de los puntos donde se realizaron los cambios. Vayamos directamente a la línea 94, donde tenemos la implementación del código responsable de procesar los mensajes procedentes de MetaTrader 5.

Ahora, presta atención, porque entender lo que ocurre en el procedimiento definido en la línea 94 será muy importante para lo que veremos más adelante. Observa que, en la línea 96, se añadió una variable estática. Esta variable dejará de existir más adelante. Pero, en este momento, nos permite almacenar localmente el precio al que se abrió la posición y conservarlo en memoria. Observa que esta misma variable se inicializa en la línea 107. Muy bien. Ahora ya tenemos el precio al que se abrió la posición. Es importante notar que este valor permanecerá constante durante todo ese tiempo. Pero lo necesitamos en un punto bastante específico. Ese punto es la línea 112. Observa que esta línea se encuentra dentro del tratador del evento CHARTEVENT_CHART_CHANGE. La razón es simple. Cuando cambias la escala de precio en el gráfico, las líneas de precio cambian junto con la escala.

Sin embargo, tenemos un problema, que veremos más adelante, cuando volvamos a la línea 25: el botón que estamos colocando en el gráfico no se mueve junto con las líneas de precio. Para resolver esto, enviamos una solicitud, como se muestra en la línea 112, con el fin de actualizar la posición del botón en el gráfico. Por eso necesitamos saber el precio al que se abrió la posición. Si, con cada cambio en la escala de precios, tuviéramos que buscar el valor al que se abrió la posición, tendríamos un retraso significativo en la ejecución de las aplicaciones del gráfico. Como el precio de la posición no cambia, podemos simplificarlo al máximo.

Sin embargo, lo que realmente nos interesa en este procedimiento DispatchMessage es el bloque siguiente. Es decir, el código implementado en la línea 114. Observa que, allí, verificamos si un objeto recibió un clic del mouse. Cuando MetaTrader 5 informe que esto ocurrió, dirá qué objeto recibió un clic. Y el nombre del objeto llega en la variable sparam. Así, en la línea 115, comprobamos si el nombre coincide con el nombre del objeto creado al inicio, es decir, en la línea siete. Si la comprobación realizada en la línea 115 resulta verdadera, en la línea 116 solicitamos a MetaTrader 5 que envíe un evento personalizado para que el Asesor Experto lo capture.

Observa los valores de dicho evento. Son los valores esperados por el Asesor Experto para interpretar la solicitud de cierre de posición del usuario u operador. Interesante, ¿verdad? Ahora, veamos cómo se crea y posiciona en el gráfico el objeto, o bitmap, que representa el botón para cerrar la posición. Para ello, en este momento vamos a centrar nuestra atención en la línea 25, donde se ejecuta la parte clave.

Observa que aquí hicimos unos pocos cambios respecto de la versión anterior. El primero fue añadir, en la línea 28, las variables x e y. Después de eso, en la línea 38, creamos nuestra línea de precio, como se hacía antes. Sin embargo, como necesitamos otro objeto que en este caso será el botón para cerrar la posición, en la línea 39 lo creamos. Observa cómo lo hacemos y qué tipo de objeto creamos. Es decir, un objeto OBJ_BITMAP_LABEL. Para posicionarlo correctamente, este objeto necesita valores de coordenadas X e Y. No podemos usar coordenadas de precio. Justo después de crear el objeto, en la línea 40, le indicamos a MetaTrader 5 que use la imagen que está incorporada como recurso en el ejecutable. Es en este punto, y no en la línea once, donde vinculamos la imagen incluida en el ejecutable y dejamos de usar la imagen del directorio. Pero, hasta este momento, solo hemos creado el objeto; todavía no lo hemos mostrado en pantalla.

Para posicionarlo en el punto correcto, necesitamos convertir coordenadas de precio-tiempo en coordenadas cartesianas. Afortunadamente, MQL5 ofrece una función de ayuda que se usa en la línea 46. Allí, convertimos el precio en una coordenada adecuada del eje Y. Muy bien, esta es la parte fácil, pero debes tener una pequeña precaución. Para entenderlo, observa lo que hacemos en la línea 48. Allí resto ocho al valor devuelto por la función de la línea 46. ¿Por qué hago esta resta y por qué uso el valor ocho? Bien, hacemos esta resta porque el objeto OBJ_BITMAP_LABEL no está anclado al centro, sino a la esquina superior izquierda.

Como no me preocupé por anclar el objeto en el centro, necesito ajustar su posición. De ahí la necesidad de restar ocho. Porque la imagen bitmap usada tiene 16 x 16 píxeles y, para centrarla, necesitamos restar ocho píxeles. Así de simple. Una vez hecho esto, podrás interactuar con el indicador de posición, para cerrar una posición haciendo clic en el objeto que acabamos de crear y posicionar. Pero podemos hacer mucho más. Sin embargo, para que el código no se vuelva insostenible, tenemos que mejorarlo, y eso haremos. Así, tendremos muchas más funcionalidades con un mínimo de codificación. Sin embargo, para separar adecuadamente los temas, veremos esto en un nuevo apartado.


Mejorando aún más el indicador

Si, querido lector y entusiasta, te detienes a pensar un poco, notarás que ya tenemos el objeto que nos permite cerrar una posición. Las líneas de precio ya se muestran en el gráfico. Tenemos la capacidad de enviar solicitudes del indicador de posición al Asesor Experto. Todo esto con un mínimo de modificaciones necesarias. Sin embargo, ¿cómo podríamos aplicar el mismo tipo de interacción usada para cerrar la posición a las líneas de Stop Loss y Take Profit, a fin de eliminar el límite de pérdida y el límite de ganancia? Y hacerlo directamente mediante la interacción con el indicador de posición, sin usar la pestaña de trading del terminal de MetaTrader 5.

Tú, querido lector y entusiasta, puedes imaginar que esto sería simple. Ya que bastaría con duplicar el código usado para cerrar la posición y adaptarlo para eliminar el Stop Loss o el Take Profit. En cierto modo, así deberías pensarlo. Sin embargo, imagina el siguiente escenario: si duplicas el código para cumplir el objetivo, tendrás éxito. Pero el trabajo necesario, si quieres seguir mejorando el indicador, será mucho mayor, ya que tendrías que actualizar todos los puntos en los que se replicó el código. En muchos casos, esta tarea es relativamente simple y exige poco esfuerzo.

Pero ¿qué tal si lo hacemos de una forma un poco más cómoda? O mejor dicho, de forma que no tengas que preocuparte por esto. ¿Bastaría con indicar que una línea representa un determinado elemento, y el propio código se encargaría de crearla de la forma correcta? Con todos los objetos ya incluidos y funcionales. Sería muy bueno, ¿verdad? Pues bien. Eso es lo que haremos en este momento. Vamos a crear un mecanismo para automatizarlo. Pero, como es una tarea que exigirá atención. Y quiero que, mi querido lector y entusiasta, entiendas qué hago y por qué lo hago. Lo veremos poco a poco. Así, podrás modificar fácilmente el código si quieres.

Lo primero que haremos, antes que nada, será añadir algunos eventos nuevos al archivo Defines.mqh. El archivo queda como se muestra a continuación. Con los eventos nuevos ya definidos.

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

Observa que todo es muy simple y práctico. No hay ninguna dificultad para entender lo que se hizo aquí. Después de hacer esto, pasaremos a la siguiente etapa. Esta, en realidad, es un poco más laboriosa en este primer momento. Iré con calma, para que realmente entiendas lo que ocurre. Muy bien, lo que queremos hacer es añadir el mecanismo que nos permite eliminar las líneas de Take Profit y Stop Loss. Pero no basta con añadir esto al bloque de mensajes, ya que no podemos ejecutar la solicitud. Por eso, necesitamos modificar el código del Asesor Experto. Para hacer este cambio, iremos directamente al punto clave, que es el archivo de encabezado C_Orders.mqh. A este archivo se le añadirá código nuevo, como se muestra a continuación.

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

El código anterior está completo, como todos los demás. Al observarlo, puedes pensar: bien, no veo ninguna gran diferencia respecto de la versión anterior. En realidad, siempre intento evitar cambios muy radicales en el código. Pero sí hay cambios aquí. Estos cambios buscan dar soporte a lo que necesitamos en este paso y en el siguiente del desarrollo. Pero centrémonos primero en este paso, que consiste en eliminar las líneas de take profit y stop loss. Observa que, en la línea 130 del código anterior, tenemos un nuevo procedimiento. Este procedimiento sirve para modificar el valor del Stop Loss y del Take Profit de una posición abierta. Presta atención a esto. En este momento, no estamos ocupándonos de órdenes que están en el libro de órdenes, sino de trabajar con una posición ya abierta. La diferencia entre ambas situaciones existe.

Básicamente, recibimos algunos parámetros y enviamos al servidor la solicitud para modificar los niveles de Take Profit y Stop Loss. En la línea 143, imprimimos en el terminal la solicitud que se envió al servidor. Esto nos permite comprobarla y analizar el funcionamiento del sistema. Bien, como este procedimiento es bastante simple y solo es necesario pasarle los argumentos, podemos pasar al siguiente punto en el que se modificó el código. Este punto está en el procedimiento de la línea 155.

En este punto procesaremos realmente las solicitudes que recibirá el Asesor Experto. Recuerda que la solicitud puede provenir de cualquier fuente, no necesariamente tiene que ser el indicador de posición que estoy mostrando. Así que estudia lo que muestro para crear tu propia solución. Observa que el código de este procedimiento DispatchMessage permanece prácticamente igual que antes. Míralo con más atención. Verás que aquí he cambiado algunas cosas. Veremos el motivo en el próximo artículo.

Observa que en la línea 175, donde comprobamos si el mensaje recibido es una solicitud de cierre de posición, se modificó la implementación del cierre. Es decir, la línea 176 se modificó ligeramente. Antes, el identificador se pasaba mediante la variable sparam; ahora uso la variable lparam. Observa que esto tiene una justificación, pero la entenderás mejor en el próximo artículo. Ahora, observa las otras dos comprobaciones que se realizan para identificar el mensaje recibido. La primera comprobación está en la línea 178, donde verificamos si el mensaje corresponde a la eliminación del take profit. La segunda comprobación está en la línea 181, donde verificamos si el mensaje es una solicitud de eliminación del stop loss.

En ambos casos, tenemos el mismo destino, es decir, el procedimiento ModifyValueSLTP, que sirve para modificar el valor de los límites de la posición. Observa con atención qué se pasa en cada parámetro. Nota que la diferencia aquí está únicamente en dónde se usa el parámetro dparam. En un caso, está en lo que sería el take profit y, en el otro, en lo que sería el Stop Loss. De esta forma, podemos eliminar uno de los límites, ya que, cuando uno de ellos reciba el valor cero, será eliminado. Esto ocurre porque el servidor de trading entiende que el valor cero representa que el límite, ya sea el take profit o el stop loss, simplemente no existe. Con esto, concluimos las modificaciones necesarias en el código del Asesor Experto, sin tener que hacer ningún otro cambio. Al menos por ahora.


Consideraciones finales

En este artículo, mostré cómo puedes implementar, de manera bastante simple y eficiente una forma de cerrar una posición usando una interacción entre el indicador Mouse Study y el indicador de posición, en combinación con el Asesor Experto debidamente codificado. Toda la implementación se realizó de una forma bastante elegante, ya que no fue necesario hacer grandes cambios en el código. También mostré toda la parte que fue necesario implementar en el código del Asesor Experto para que podamos crear un mecanismo destinado a eliminar las líneas de Take Profit y Stop Loss.

Sin embargo, debido al mayor número de cambios que deberán realizarse en el código del indicador de posición, no presentaré en este artículo el código ya modificado. Lo presentaré y explicaré debidamente en el próximo artículo. Lo haré para que, mi querido lector y entusiasta, puedas entender realmente cómo y por qué funciona el código. Pero, principalmente, por qué MetaTrader 5 puede interpretar y presentar correctamente la información mientras operas. Verás que no necesitamos preocuparnos de ciertos detalles. Gran parte de lo que realmente debemos hacer consistirá en indicarle a MetaTrader 5 que envíe un mensaje, y el resto se ejecutará de forma natural, sin grandes esfuerzos ni preocupaciones.

Espero que, tengas más experiencia en MQL5 o estés empezando, comprendas que aquí mi intención es enseñar cómo podemos implementar este tipo de soluciones. Si te ha despertado curiosidad, estudia este artículo y el código con cuidado, porque en el próximo veremos la parte del indicador de posición.

ArchivoDescripción
Experts\Expert Advisor.mq5
Muestra la interacción entre Chart Trade y el Asesor Experto. (Es necesario usar Mouse Study para la interacción)
Indicators\Chart Trade.mq5Crea la ventana para configurar la orden que se va a enviar. (Es necesario usar Mouse Study para la interacción)
Indicators\Market Replay.mq5Crea los controles para la interacción con el servicio de repetición/simulador. (Es necesario usar Mouse Study para la interacción)
Indicators\Mouse Study.mq5Permite la interacción entre los controles gráficos y el usuario (Necesario tanto para operar el sistema de repetición/simulador como en el mercado real)
Indicators\Order Indicator.mq5Responsable de indicar órdenes de mercado, permitiendo interactuar con ellas y controlarlas.
Indicators\Position View.mq5Responsable de indicar posiciones de mercado, permitiendo su interacción y control.
Services\Market Replay.mq5Crea y mantiene el servicio de repetición y simulación de mercado (Archivo principal de todo el sistema)

Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/13206

Archivos adjuntos |
Anexo.zip (779.24 KB)
Del básico al intermedio: Como burbujas de jabón Del básico al intermedio: Como burbujas de jabón
En este artículo, se explicará un mecanismo muy simple y fácil de entender, cuyo propósito es ordenar un array cualquiera. En él, veremos que no siempre el resultado obtenido es el que esperamos tener, por lo que será necesario adaptar la propia implementación para conseguir los resultados adecuados.
De novato a experto: Noticias animadas utilizando MQL5 (VI) Estrategia de órdenes pendientes para el trading basado en noticias De novato a experto: Noticias animadas utilizando MQL5 (VI) Estrategia de órdenes pendientes para el trading basado en noticias
En este artículo, nos centramos en la integración de una lógica de ejecución de órdenes basada en las noticias, lo que permite que el asesor experto actúe, y no solo informe. Acompáñanos a descubrir cómo implementar la ejecución automática de operaciones en MQL5 y convertir el asesor experto «News Headline» en un sistema de trading plenamente automatizado y capaz de responder en tiempo real. Los Asesores Expertos ofrecen ventajas significativas para los desarrolladores de algoritmos gracias a la amplia gama de funciones que admiten. Hasta ahora, nos hemos centrado en desarrollar una herramienta de presentación de noticias y eventos del calendario, que incluye paneles de análisis basados en inteligencia artificial e indicadores técnicos.
Optimización Extrema — Extremal Optimization (EO) Optimización Extrema — Extremal Optimization (EO)
En este artículo analizamos el algoritmo de Optimización Extremal (OE), un método de optimización inspirado en el modelo de criticidad autoorganizada de Bak-Sneppen, donde la evolución se produce mediante la eliminación de los componentes del sistema que representan el peor caso. La versión modificada del algoritmo para poblaciones específicas demuestra un cambio de enfoque, alejándose de los principios teóricos en favor de la eficiencia práctica, lo cual lleva a la creación de potentes herramientas computacionales
Del básico al intermedio: Navegando por la SandBox Del básico al intermedio: Navegando por la SandBox
En este artículo veremos dos formas de observar e incluso tener cierta interacción con el contenido de una SandBox, tomando MetaTrader 5 como base. Entender el contenido que se muestra en este artículo será fundamental para entender lo que se verá en los próximos artículos.