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

Simulación de mercado: Position View (VI)

MetaTrader 5Probador |
32 0
Daniel Jose
Daniel Jose

Introducción

Hola a todos, bienvenidos a un nuevo 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 (V), expliqué cómo puedes obtener los datos necesarios para entender qué sucede en una aplicación. Esto permitirá implementar el sistema usando la función OnTradeTransaction. Pues bien, allí solo se presentó cómo hacerlo en el Asesor Experto. Al revisar aquellos datos y el Asesor Experto, mi estimado lector, seguramente te habrás quedado bastante confundido y con dudas sobre si eso es práctico. Un poco de escepticismo no hace ningún daño. También es aconsejable y saludable tener algunas dudas cuando nos encontramos con algo completamente nuevo.

Así, en este artículo implementaremos los primeros pasos. Esto permitirá que aquellos mismos datos, vistos en el artículo anterior, tengan algún sentido. Como lo que veremos es algo que, a primera vista, puede resultar un poco confuso, quiero que estudies este artículo con calma más adelante, haciendo pruebas para intentar comprender lo que se explicará. No uses, bajo ningún concepto, lo que veremos en este artículo en una cuenta real sin haber comprendido realmente qué sucede, ya que las posibilidades de sufrir pérdidas y errores al interpretar los datos que se mostrarán en el gráfico pueden hacer que tengas una experiencia muy desagradable con este sistema que estamos desarrollando.

Una vez más: no uses este sistema que construiremos sin entender qué ocurre. Para ello, experimenta y estudia bastante el código, ya que aquí su finalidad es ser lo más didáctico posible. La eficiencia, en este caso, es solo un detalle. La premisa principal es que el código sea lo más simple posible, para que todos puedan entender qué ocurre.


Entendamos lo que haremos

Muchos de ustedes probablemente estén acostumbrados a ver o hacer siempre el mismo tipo de tareas. No es que esté mal hacerlo así. Pero, si podemos hacerlo de otra manera, ¿por qué no intentarlo? Este es precisamente el punto que hay que entender aquí. La tarea principal de un Asesor Experto no es colocar objetos en el gráfico ni hacer cálculos. Incluso puedes hacerlo así. No es incorrecto. Pero la tarea principal de un Asesor Experto es crear un medio para comunicarnos adecuadamente con el servidor de trading. Por esta razón, solo podemos colocar un único Asesor Experto en cada gráfico. Sin embargo, incluir en el Asesor Experto tareas como añadir objetos o manipular esos mismos objetos en el gráfico no es algo tan adecuado.

Una vez más, insisto en esto: no es que sea incorrecto implementar esas tareas en un Asesor Experto. Pero piensa que puedes tener más de un Asesor Experto, cada uno pensado para usarse en un escenario o mercado concreto. No es muy práctico que pongas todo en un único código, ya que, si decides colocar un sistema de visualización de posiciones en cada Asesor Experto, al perfeccionar este mismo sistema en otro Asesor Experto acabarás sintiéndote un poco insatisfecho cuando uses la versión anterior, ya que el código tendrá que transferirse entre cada uno de los Asesores Expertos existentes.

Por esta razón, propongo usar un indicador para esto. Pero el indicador, como seguramente ya notaste en estos últimos artículos, no puede hacer ciertas cosas. No es posible, ni aconsejable, que intentes forzar el indicador a realizar determinadas tareas. Sin embargo, podemos crear perfectamente un indicador que nos permita visualizar en el gráfico las posiciones abiertas. Necesitamos crear mecanismos que permitan usar los indicadores de forma más sostenible y menos confusa.

Uno de esos mecanismos es el intercambio de mensajes entre el indicador y el Asesor Experto. Ya llevamos algún tiempo usando este mecanismo. Esto ocurre porque tanto el indicador Chart Trade como el indicador de mouse usan ese mismo mecanismo para comunicarse con el Asesor Experto. Pero aún no hemos creado, o mejor dicho, aún no hemos empezado a desarrollar ese mecanismo para permitir que el indicador de posición se comunique con el Asesor Experto.


Una pequeña mejora

Lo primero que necesitamos es dotar al Asesor Experto de la capacidad de añadir un indicador de posición en el gráfico cada vez que se abra una nueva posición. Esto es relativamente simple de hacer, ya que podemos usar el mismo mecanismo que se usa cuando el Asesor Experto se coloca en el gráfico. Si observas los artículos anteriores, notarás que, al colocar el Asesor Experto en el gráfico, este buscará, entre las posiciones abiertas, cuál corresponde al criterio que este indica y espera. Esto permite que el Asesor Experto coloque adecuadamente un indicador de posición en el gráfico.

Como se vio en los artículos anteriores, cuando el Asesor Experto se elimina del gráfico, por cualquier motivo, eliminará los indicadores de posición colocados en el gráfico. Bien. Esto ocurrirá sin problemas si el Asesor Experto se retira adecuadamente. En caso de un fallo del programa, dicha eliminación no ocurrirá. Así, cuando el Asesor Experto vuelva a colocarse en el gráfico, podemos tener algún residuo que deberá eliminarse del gráfico. Ese residuo es precisamente el indicador de posición. Recuerda una cosa: vamos a asumir que el operador no tocó ningún elemento del gráfico. Solo vio que el Asesor Experto salió del gráfico dejando algún indicador de posición en él.

Entonces, cuando el operador vuelva a colocar el Asesor Experto en el gráfico, puede haber cambiado algo en el escenario. O mejor dicho: antes del crash, el Asesor Experto estaba configurado para observar mini contratos. Sin embargo, cuando el operador lo vuelve a colocar en el gráfico, el Asesor Experto puede estar configurado para monitorear contratos completos. No obstante, algún indicador de posición que había en el gráfico mostraba una posición de mini contrato. En este momento, podremos tener problemas. Por esta razón, para evitarlo, necesitamos modificar o, mejor dicho, añadir una línea de código en la rutina de inicialización del Asesor Experto. Esta línea permitirá limpiar posibles indicadores residuales.

Esto es muy simple y directo. En el siguiente fragmento puedes ver cómo se consigue.

15. //+------------------------------------------------------------------+
16. C_Orders       *Orders;
17. C_Terminal     *Terminal;
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     Terminal = NULL;
22.     Orders = new C_Orders(0xC0DEDAFE78514269);
23.     
24.     return INIT_SUCCEEDED;
25. }
26. //+------------------------------------------------------------------+
27. void OnTick() {}
28. //+------------------------------------------------------------------+
29. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
30. {
31.     int handle;
32.     
33.     (*Orders).DispatchMessage(id, lparam, dparam, sparam);
34.     switch (id)
35.     {
36.         case CHARTEVENT_CHART_CHANGE:
37.             if (Terminal != NULL) break;
38.             else
39.             {
40.                 ulong ul;
41.                 Terminal = new C_Terminal(0, 0, user00);
42.                 for (int count = PositionsTotal() - 1; count >= 0; count--)
43.                 {
44.                     ul = PositionGetTicket(count);
45.                     if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol)
46.                     {
47.                         ChartIndicatorDelete(0, 0, IntegerToString(ul));
48.                         continue;
49.                     }
50.                     handle = iCustom(NULL, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul);
51.                     ChartIndicatorAdd(0, 0, handle);
52.                     IndicatorRelease(handle);
53.                 }
54.             }
55.         case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
56.             EventChartCustom(0, evEA_At_ChartTrade, user00, 0, "");
57.             break;
58.     }
59. }
60. //+------------------------------------------------------------------+

Fragmento del código del Asesor Experto

Observa precisamente la línea 47 del fragmento anterior. Es precisamente la responsable de evitar lo que acabamos de describir. Es decir, cuando el Asesor Experto vuelva a colocarse en el gráfico, usará esta línea 47 para eliminar los indicadores antiguos que posiblemente no correspondan al criterio de inicialización. Es decir, si el operador cambia el tipo de contrato, el Asesor Experto lo detectará y eliminará los indicadores de posición que no correspondan a lo esperado. Si el indicador corresponde al tipo de contrato, no habrá problema, no ocurrirá nada con él. A lo sumo, devolverá un error al Asesor Experto. Pero dicho error es perfectamente admisible y podemos ignorarlo.

Muy bien. Ahora ya tenemos cierta consistencia en los datos que podrán estar presentes en el gráfico. Podemos pasar a la siguiente etapa, que implica el envío de mensajes entre el Asesor Experto y el indicador de posición.


Implementación de la comunicación entre el Asesor Experto y el indicador de posición

En este momento, comenzaremos a implementar los primeros intercambios de información entre estas dos aplicaciones. Como todo intercambio de mensajes implica un gran número de aspectos que debemos analizar, lo haremos poco a poco. Empezaremos por lo más básico y profundizaremos cada vez más, hasta conseguir un indicador de posición totalmente funcional.

Como nuestro indicador de posición, en este momento, solo cuenta con líneas horizontales, los llamados objetos HLINE, no necesitamos transmitir muchos mensajes. Pero sí necesitamos hacer algunos ajustes adicionales en el código. Comenzando por el archivo de encabezado Defines.mqh, que 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.                         };
49. //+------------------------------------------------------------------+
50. enum EnumPriority {                          //Priority list on objects
51.             ePriorityNull = -1,
52.             ePriorityDefault = 0,
53.             ePriorityChartTrade = 5,
54.             ePriorityOrders = 10
55.                         };
56. //+------------------------------------------------------------------+

Defines.mqh

En este archivo, añadimos una nueva enumeración, que define el evento encargado de activar una llamada de actualización del indicador de posición. Dicho evento está definido en la línea 47. Hecho esto, podemos pasar al código del Asesor Experto. El código completo del Asesor Experto se muestra íntegramente a continuación.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
004. #property description "Demo version between interaction"
005. #property description "of Chart Trade and Expert Advisor"
006. #property version   "1.118"
007. #property link "https://www.mql5.com/pt/articles/13203"
008. //+------------------------------------------------------------------+
009. #include <Market Replay\Order System\C_Orders.mqh>
010. #include <Market Replay\Auxiliar\C_Terminal.mqh>
011. //+------------------------------------------------------------------+
012. enum eTypeContract {MINI, FULL};
013. //+------------------------------------------------------------------+
014. input eTypeContract user00 = MINI;                 //Cross order in contract
015. //+------------------------------------------------------------------+
016. C_Orders           *Orders;
017. C_Terminal         *Terminal;
018. //+------------------------------------------------------------------+
019. int OnInit()
020. {
021.    Terminal = NULL;
022.    Orders = new C_Orders(0xC0DEDAFE78514269);
023.    
024.    return INIT_SUCCEEDED;
025. }
026. //+------------------------------------------------------------------+
027. void OnTick() {}
028. //+------------------------------------------------------------------+
029. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
030. {
031.    int handle;
032.    
033.    (*Orders).DispatchMessage(id, lparam, dparam, sparam);
034.    switch (id)
035.    {
036.            case CHARTEVENT_CHART_CHANGE:
037.                    if (Terminal != NULL) break;
038.                    else
039.                    {
040.                            ulong ul;
041.                            Terminal = new C_Terminal(0, 0, user00);
042.                            for (int count = PositionsTotal() - 1; count >= 0; count--)
043.                            {
044.                                    ul = PositionGetTicket(count);
045.                                    if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol)
046.                                    {
047.                                            ChartIndicatorDelete(0, 0, IntegerToString(ul));
048.                                            continue;
049.                                    }
050.                                    handle = iCustom(NULL, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", ul);
051.                                    ChartIndicatorAdd(0, 0, handle);
052.                                    IndicatorRelease(handle);
053.                            }
054.                    }
055.            case CHARTEVENT_CUSTOM + evChartTrade_At_EA:
056.                    EventChartCustom(0, evEA_At_ChartTrade, user00, 0, "");
057.                    break;
058.    }
059. }
060. //+------------------------------------------------------------------+
061. void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
062. {
063.    if (Terminal == NULL) return;
064.    static ulong ticket = 0;
065.    switch (trans.type)
066.    {
067.            case TRADE_TRANSACTION_HISTORY_ADD:
068.                    ticket = (trans.order != trans.position ? trans.position : 0);
069.                    break;
070.            case TRADE_TRANSACTION_REQUEST:
071.                    if ((request.symbol == (*Terminal).GetInfoTerminal().szSymbol) && (result.retcode == TRADE_RETCODE_DONE)) switch (request.action)
072.                    {
073.                            case TRADE_ACTION_DEAL:
074.                                    if (ticket) EventChartCustom(0, evUpdate_Position, ticket, 0, "");
075.                                    else
076.                                    {
077.                                            int handle = iCustom(NULL, PERIOD_CURRENT, "\\Indicators\\Position View.ex5", result.order);
078.                                            ChartIndicatorAdd(0, 0, handle);
079.                                            IndicatorRelease(handle);
080.                                    }
081.                                    ticket = 0;
082.                                    break;
083.                            case TRADE_ACTION_SLTP:
084.                                    EventChartCustom(0, evUpdate_Position, request.position, 0, "");
085.                                    break;
086.                    }
087.                    break;
088.    };
089. }
090. //+------------------------------------------------------------------+
091. void OnDeinit(const int reason)
092. {
093.    ulong ul;
094.    
095.    switch (reason)
096.    {
097.            case REASON_REMOVE:
098.            case REASON_INITFAILED:
099.                    EventChartCustom(0, evEA_At_ChartTrade, -1, 0, "");
100.                    break;
101.    }
102.    if (Terminal != NULL) for (int count = PositionsTotal() - 1; count >= 0; count--)
103.    {
104.            ul = PositionGetTicket(count);
105.            if (PositionGetString(POSITION_SYMBOL) != (*Terminal).GetInfoTerminal().szSymbol) continue;
106.            ChartIndicatorDelete(0, 0, IntegerToString(ul));
107.    }
108.    delete Orders;
109.    delete Terminal;
110. }
111. //+------------------------------------------------------------------+

Expert Advisor.mq5

Gran parte de este código ya se explicó anteriormente. Por lo tanto, debemos centrarnos en lo nuevo aquí. Es decir, en la función OnTradeTransaction. En este momento, no te preocupes por algunas cosas que quizá todavía no comprendas. Explicaré todo con calma y poco a poco. Pero lo que quiero destacar aquí es la presencia de la función EventChartCustom. Esta función aparece en las líneas 74 y 84. Estas líneas son precisamente las que permiten que el Asesor Experto comunique al indicador de posición que algo ocurrió y que el indicador de posición necesita actualizarse.

Por ahora, no te preocupes por cómo se implementará esto en el indicador. Solo observa cuándo el Asesor Experto informa al indicador que este necesita actualizarse. Para entenderlo mejor, es necesario comprender lo que se vio en el artículo anterior. Si tienes alguna duda con respecto a alguna información, consulta, en el artículo anterior, cómo puedes buscar la información necesaria para que todo tenga sentido.

Aquí trataremos tres situaciones distintas. La primera situación ocurre cuando, mediante el indicador Chart Trade, pedimos al Asesor Experto que realice una compra o venta a mercado. Cuando se haga esto, la variable estática ticket tendrá valor cero. El motivo es que la línea 68 contendrá valores idénticos para trans.order y trans.position. Bien. Así, cuando el servidor notifique un TRADE_TRANSACTION_REQUEST, se aplicará un tratamiento adecuado a la operación realizada. Recuerda: aún estamos abriendo la posición. De esta forma, cuando el servidor genere TRADE_ACTION_DEAL, la comprobación de la línea 74 fallará. Esto se debe a que la variable ticket vale cero. En ese caso, se ejecutará el código entre las líneas 76 y 80. Observa que es el mismo código usado en la inicialización del Asesor Experto, para indicarle a MetaTrader 5 que queremos añadir un indicador de posición al gráfico.

Al final, como no sabemos el motivo del disparo de TRADE_ACTION_DEAL, volvemos a poner la variable ticket en cero. Y listo. El indicador de posición aparecerá en el gráfico, mostrando la misma información que verías si MetaTrader 5 pudiera ofrecer soporte en este caso. Recuerda una vez más: MetaTrader 5 no cubre adecuadamente el sistema de órdenes cruzadas, el llamado cross order. Por esta razón, cuando usamos esta modalidad operativa, necesitamos crear una forma de representar esos datos en el gráfico. Para eso sirve el indicador de posición.

Muy bien, nuestra posición se creó y está en el gráfico. Después de algún tiempo, el operador decide que es hora de aumentar la posición. Entonces, vuelve a usar el indicador Chart Trade y pide que aumente el volumen de la posición. Aquí hay un punto importante. En cuentas de tipo HEDGING, esta nueva entrada o solicitud de aumento de una posición no hará que el volumen cambie realmente. Lo que ocurre, en realidad, es que se abrirá una nueva posición. Como no hacemos una distinción entre una cuenta HEDGING y una NETTING, lo que ocurre al pedir un aumento del volumen en una cuenta HEDGING es precisamente lo que acabo de explicar para la apertura de una posición. Sin embargo, al hacer el mismo tipo de solicitud, es decir, un aumento de posición en una cuenta NETTING, la situación será un poco diferente. En este caso, cuando el servidor genere una TRADE_TRANSACTION_HISTORY_ADD, tendremos valores diferentes entre trans.order y trans.position. Cuando esto ocurre, significa que se produjo algún cambio en la posición abierta. Así, el valor de la variable ticket ya no será cero. En este caso, ticket tendrá el valor de la posición afectada. En un momento determinado, el servidor generará una TRADE_TRANSACTION_REQUEST en respuesta a la solicitud efectuada.

Cuando esto ocurra y se procese una acción TRADE_ACTION_DEAL, la prueba de la línea 74 tendrá éxito. En este momento, se disparará un evento personalizado, indicando al indicador de posición que deberá actualizarse. Observa que no dirigimos el evento a un indicador o aplicación específicos. Todas las aplicaciones que estén en el gráfico recibirán este mismo evento personalizado. Sin embargo, como filtramos este evento dentro de cada aplicación, solo el indicador o la aplicación correcta entenderá el mensaje. La tarea del Asesor Experto termina en este punto. En realidad, no tiene idea de si el indicador de posición entenderá o no el mensaje. Ni siquiera sabe si el indicador está en el gráfico. Para el Asesor Experto, eso tiene poca importancia. Cumplió su tarea, y eso es todo. Lo mismo ocurre cuando cambias los valores de take profit y stop loss de una posición. En este caso, tendremos la ejecución de la acción TRADE_ACTION_SLTP, que aparece en la línea 83. Observa que, en la línea 84, hacemos lo mismo que cuando tenemos un aumento de la posición o una realización parcial de un volumen determinado. Esto se aplica si usas una cuenta de tipo NETTING. Bien.

Pero ¿qué ocurre cuando se cierra una posición? Creo que, mi estimado lector, esperarías ver aquí, en el Asesor Experto, un código encargado de eliminar el indicador de posición del gráfico. Sin embargo, no es exactamente eso lo que puede verse en el código del Asesor Experto. Entonces, ¿cómo consigue eliminar el indicador del gráfico? De forma simple. Hace lo mismo que se hace en el cambio de volumen en cuentas NETTING. Es decir, tendremos, en la línea 68, un valor diferente entre trans.order y trans.position. Con esto, la comprobación de la línea 74 tendrá éxito, y enviará así una solicitud para que el indicador se actualice. Pero, como la posición se cerró, el indicador se eliminará del gráfico. Para entender esto por completo, necesitamos ver el código del indicador de posición. Para separar los temas, lo veremos en un nuevo apartado.


Indicador de posición mejorado

Muy bien, como debes saber, mi estimado lector, el código del indicador de posición, por ahora, se compone de dos partes. La primera puede verse íntegramente a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Positions.ico"
04. #property description "Indicator for tracking an open position on the server."
05. #property description "This should preferably be used together with an Expert Advisor."
06. #property description "For more details see the same article."
07. #property version   "1.118"
08. #property link "https://www.mql5.com/pt/articles/13203"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. //+------------------------------------------------------------------+
12. #define def_ShortName "Position View"
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Order System\C_IndicatorPosition.mqh>
15. #include <Market Replay\Defines.mqh>
16. //+------------------------------------------------------------------+
17. input ulong user00 = 0;                 //For Expert Advisor use
18. input color user01 = clrRoyalBlue;      //Color Line Price
19. input color user02 = clrForestGreen;    //Color Line Take Profit
20. input color user03 = clrFireBrick;      //Color Line Stop Loss
21. //+------------------------------------------------------------------+
22. C_IndicatorPosition *Positions = NULL;
23. //+------------------------------------------------------------------+
24. int OnInit()
25. {
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     Positions = new C_IndicatorPosition(user01, user02, user03);
28.     if (!Positions.CheckCatch(user00))
29.     {
30.         ChartIndicatorDelete(0, 0, def_ShortName);
31.         return INIT_FAILED;
32.     }
33. 
34.     return INIT_SUCCEEDED;
35. }
36. //+------------------------------------------------------------------+
37. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
38. {
39.     return rates_total;
40. }
41. //+------------------------------------------------------------------+
42. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
43. {
44.     (*Positions).DispatchMessage(id, lparam, dparam, sparam);
45. };
46. //+------------------------------------------------------------------+
47. void OnDeinit(const int reason)
48. {
49.     delete Positions;
50. }
51. //+------------------------------------------------------------------+

Position View.mq5

Este es el código principal del indicador. Observa que, básicamente, lo único diferente que tenemos aquí es la función OnChartEvent. Como necesitamos cierta información interna de la clase C_IndicatorPosition, no hacemos nada en este código principal. Simplemente hacemos una llamada al procedimiento DispatchMessage. Por lo tanto, solo con observar este código, no tenemos forma de entender cómo funciona la comunicación con el Asesor Experto. Veamos entonces el archivo de encabezado C_IndicatorPosition.mqh, que puede verse íntegramente a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SufixTake    "Take"
05. #define def_SufixStop    "Stop"
06. //+------------------------------------------------------------------+
07. #include "..\Auxiliar\C_Terminal.mqh"
08. //+------------------------------------------------------------------+
09. class C_IndicatorPosition : private C_Terminal
10. {
11.     private    :
12.         struct st00
13.         {
14.             ulong     ticket;
15.             color     corPrice, corTake, corStop;
16.             string    szShortName;
17.         }m_Infos;
18. //+------------------------------------------------------------------+
19.         inline void MoveLineInfos(string szObjName, const double price)
20.         {
21.             szObjName = m_Infos.szShortName + szObjName;
22.             if (price <= 0) ObjectDelete(0, szObjName);
23.             else ObjectSetDouble(0, szObjName, OBJPROP_PRICE, price);
24.         }
25. //+------------------------------------------------------------------+
26.         void CreateLineInfos(string szObjName, const color cor, const string szDescription = "\n")
27.         {            
28.             szObjName = m_Infos.szShortName + szObjName;
29.             CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop))));
30.             ObjectSetString(0, szObjName, OBJPROP_TEXT, szDescription);
31.             ObjectSetString(0, szObjName, OBJPROP_TOOLTIP, szDescription);
32.             ObjectSetInteger(0, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice);
33.         }
34. //+------------------------------------------------------------------+
35.     public    :
36. //+------------------------------------------------------------------+
37.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
38.             :C_Terminal()
39.         {
40.             ZeroMemory(m_Infos);
41.             m_Infos.corPrice = corPrice;
42.             m_Infos.corTake  = corTake;
43.             m_Infos.corStop  = corStop;
44.         }
45. //+------------------------------------------------------------------+
46.         ~C_IndicatorPosition()
47.         {
48.             if (m_Infos.ticket != 0)
49.                 ObjectsDeleteAll(0, IntegerToString(m_Infos.ticket));
50.         }
51. //+------------------------------------------------------------------+
52.         bool CheckCatch(ulong ticket)
53.         {
54.             m_Infos.szShortName = IntegerToString(m_Infos.ticket = ticket);            
55.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
56.             if (ObjectFind(0, m_Infos.szShortName) >= 0)
57.             {
58.                 m_Infos.ticket = 0;
59.                 return false;
60.             }
61.             IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
62.             CreateLineInfos(NULL, m_Infos.corPrice, "Position opening price.");
63.             CreateLineInfos(def_SufixTake, m_Infos.corTake, "Take Profit point.");
64.             CreateLineInfos(def_SufixStop, m_Infos.corStop, "Stop Loss point.");
65.             EventChartCustom(0, evUpdate_Position, ticket, 0, "");
66.             
67.             return true;
68.         }
69. //+------------------------------------------------------------------+
70.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
71.         {
72.             switch (id)
73.             {
74.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
75.                     if (lparam != m_Infos.ticket) return;
76.                     if (!PositionSelectByTicket(m_Infos.ticket))
77.                     {
78.                         ChartIndicatorDelete(0, 0,  m_Infos.szShortName);
79.                         return;
80.                     };
81.                     MoveLineInfos(NULL, PositionGetDouble(POSITION_PRICE_OPEN));
82.                     MoveLineInfos(def_SufixTake, PositionGetDouble(POSITION_TP));
83.                     MoveLineInfos(def_SufixStop, PositionGetDouble(POSITION_SL));
84.                 break;
85.             }
86.         }
87. //+------------------------------------------------------------------+
88. };
89. //+------------------------------------------------------------------+
90. #undef def_SufixTake
91. #undef def_SufixStop
92. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

Aunque, estimado lector, puedas imaginar que las cosas deberían hacerse de una manera, aquí, lo implementaremos de una forma un poco diferente de lo que muchos consideran más habitual. Básicamente, en este momento, no hice muchos cambios en el código. Pero puedes notar que hay elementos diferentes aquí. Para empezar, en la línea 19 tenemos un nuevo procedimiento. Su objetivo es cambiar el punto en el que se encuentra la línea horizontal del precio. No hay mucho que decir sobre este procedimiento. Esto se debe a que, cuando el precio sea igual a cero o negativo, se eliminará el objeto de línea. Esto se hace en la línea 22 del código anterior.

Sin embargo, conviene recordar que, aunque normalmente no existe la posibilidad de un precio negativo, esto no se aplica a los índices, o mejor dicho: cuando usas una combinación de dos o más valores para generar un nuevo valor, sí puede haber valores negativos. Pero dicho valor no se refiere a un precio negativo, sino a un índice o relación negativa. Si tienes problemas por usar un gráfico de relación entre valores, tendrás que ajustar este indicador de posición. Por ahora, no estamos centrados en hacer que trabaje con relaciones, sino con precios. En el futuro mostraré cómo podemos trabajar con relaciones en MetaTrader 5.

Pero vayamos paso a paso. Primero, veamos cómo hacer que este indicador de posición funcione. Bien. Si el precio es válido, en la línea 23 le indicaremos al objeto HLINE dónde deberá ubicarse. Así de simple. Ahora veamos algo curioso en la función de la línea 52. Observa que, en la línea 65, se dispara un evento personalizado. Pero ¿por qué se hace esto? El motivo es: programa una vez y prueba siempre. Como este indicador recibirá mensajes, o eventos personalizados, podemos hacer que, al colocarse en el gráfico, también tenga el mismo comportamiento que cuando reciba una solicitud de actualización del Asesor Experto.

Como indiqué en el tema anterior, el Asesor Experto no verificará si el indicador de posición cumplió o no su función. Simplemente dispara el evento y se ocupa de otras tareas. Corresponde al indicador de posición realizar todo lo restante. Por esta razón, conviene tener una metodología clara de implementación. Si el indicador de posición hace su trabajo al colocarse manualmente en el gráfico, pero no logra interpretar lo que solicita el Asesor Experto, pronto podrás concluir que existe una falla de comunicación entre el Asesor Experto y el indicador. Podrás concluirlo porque los eventos se manejan adecuadamente cuando el indicador se coloca manualmente en el gráfico.

Así, de la misma manera que el Asesor Experto dispara un evento para que el indicador de posición efectúe su tarea, el indicador de posición, al comprobar si puede o no colocarse en el gráfico, también disparará el mismo tipo de evento. De esta manera, en ambas situaciones tendremos la llamada de la línea 70 en el código mostrado anteriormente.

Este código de la línea 70 es bastante interesante. Por ahora, solo nos preocupamos por manejar un único evento: el evento evUpdate_Position, cuyo tratamiento se inicia en la línea 74. Pues bien, en la línea 75 verificamos si el ticket de la posición informado en la llamada del evento coincide con el que espera el indicador. Si los tickets son diferentes, interrumpimos el procesamiento aquí. Es importante verificar si el ticket es el mismo, ya que el mensaje de evento se envía a todas las aplicaciones que están en el gráfico. Por eso, es importante filtrar correctamente los eventos antes de usar los datos. Una vez realizado el filtrado, podemos verificar, en la línea 76, si la posición existe. Recuerda: el Asesor Experto no dice si estamos actualizando, cerrando o abriendo una nueva posición. Solo dice: indicador de posición, tengo algo para ti. Haz lo que sea necesario. Eso es todo.

Todo lo demás es responsabilidad del indicador, que debe ejecutar y verificar lo que corresponda. Si la posición no existe, en la línea 78 finalizamos el indicador y este se eliminará del gráfico. Pero si la posición se está creando o actualizando, se desplazarán las líneas de precio. Esto se hace en las líneas 81 a 83. Así funciona el mecanismo. Sin embargo, este mismo código mostrado anteriormente tiene un problema. Es necesario que lo entiendas antes de intentar comprender el código que veremos en el próximo tema.


¿Tiene que ser así?

En el código de la clase C_IndicatorPosition, visto anteriormente, tenemos un problema. En realidad, es más un inconveniente que un problema. El detalle es que, si eliminas las líneas de stop loss y take profit, o no las añades, y después de un tiempo decides añadirlas al gráfico, actualizando así la posición, no podrás hacerlo directamente. La clase vista anteriormente no puede hacerlo. Sin embargo, con pocos cambios en el código, podemos empezar a hacerlo. Estos cambios se muestran en el código a continuación.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SufixTake    "Take"
05. #define def_SufixStop    "Stop"
06. //+------------------------------------------------------------------+
07. #include "..\Auxiliar\C_Terminal.mqh"
08. //+------------------------------------------------------------------+
09. class C_IndicatorPosition : private C_Terminal
10. {
11.     private    :
12.         struct st00
13.         {
14.             ulong     ticket;
15.             color     corPrice, corTake, corStop;
16.             string    szShortName;
17.         }m_Infos;
18. //+------------------------------------------------------------------+
19.         inline void MoveLineInfos(const string szDefine, const double price)
20.         {
21.             string szName = m_Infos.szShortName + szDefine;
22.             if (price <= 0) ObjectDelete(0, szName);
23.             else
24.             {
25.                 if (ObjectFind(0, szName) < 0)
26.                 {
27.                     if (szDefine == def_SufixTake) CreateLineInfos(def_SufixTake, m_Infos.corTake, "Take Profit point."); else
28.                     if (szDefine == def_SufixStop) CreateLineInfos(def_SufixStop, m_Infos.corStop, "Stop Loss point."); else
29.                     CreateLineInfos(NULL, m_Infos.corPrice, "Position opening price.");
30.                 }                    
31.                 ObjectSetDouble(0, szName, OBJPROP_PRICE, price);
32.             }
33.         }
34. //+------------------------------------------------------------------+
35.         void CreateLineInfos(string szObjName, const color cor, const string szDescription = "\n")
36.         {            
37.             szObjName = m_Infos.szShortName + szObjName;
38.             CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == m_Infos.corPrice ? ePriorityNull : (ePriorityOrders + (cor == m_Infos.corStop))));
39.             ObjectSetString(0, szObjName, OBJPROP_TEXT, szDescription);
40.             ObjectSetString(0, szObjName, OBJPROP_TOOLTIP, szDescription);
41.             ObjectSetInteger(0, szObjName, OBJPROP_SELECTABLE, cor != m_Infos.corPrice);
42.         }
43. //+------------------------------------------------------------------+
44.     public    :
45. //+------------------------------------------------------------------+
46.         C_IndicatorPosition(color corPrice, color corTake, color corStop)
47.             :C_Terminal()
48.         {
49.             ZeroMemory(m_Infos);
50.             m_Infos.corPrice = corPrice;
51.             m_Infos.corTake  = corTake;
52.             m_Infos.corStop  = corStop;
53.         }
54. //+------------------------------------------------------------------+
55.         ~C_IndicatorPosition()
56.         {
57.             if (m_Infos.ticket != 0)
58.                 ObjectsDeleteAll(0, IntegerToString(m_Infos.ticket));
59.         }
60. //+------------------------------------------------------------------+
61.         bool CheckCatch(ulong ticket)
62.         {
63.             m_Infos.szShortName = IntegerToString(m_Infos.ticket = ticket);            
64.             if (!PositionSelectByTicket(m_Infos.ticket)) return false;
65.             if (ObjectFind(0, m_Infos.szShortName) >= 0)
66.             {
67.                 m_Infos.ticket = 0;
68.                 return false;
69.             }
70.             IndicatorSetString(INDICATOR_SHORTNAME, m_Infos.szShortName);
71.             EventChartCustom(0, evUpdate_Position, ticket, 0, "");
72.             
73.             return true;
74.         }
75. //+------------------------------------------------------------------+
76.         void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
77.         {
78.             switch (id)
79.             {
80.                 case CHARTEVENT_CUSTOM + evUpdate_Position:
81.                     if (lparam != m_Infos.ticket) return;
82.                     if (!PositionSelectByTicket(m_Infos.ticket))
83.                     {
84.                         ChartIndicatorDelete(0, 0,  m_Infos.szShortName);
85.                         return;
86.                     };
87.                     MoveLineInfos(NULL, PositionGetDouble(POSITION_PRICE_OPEN));
88.                     MoveLineInfos(def_SufixTake, PositionGetDouble(POSITION_TP));
89.                     MoveLineInfos(def_SufixStop, PositionGetDouble(POSITION_SL));
90.                 break;
91.             }
92.             ChartRedraw();
93.         }
94. //+------------------------------------------------------------------+
95. };
96. //+------------------------------------------------------------------+
97. #undef def_SufixTake
98. #undef def_SufixStop
99. //+------------------------------------------------------------------+

C_IndicatorPosition.mqh

Este código anterior deberá colocarse en lugar del código original visto en el tema anterior. Puedes notar que los cambios fueron pocos. Pero son suficientes para permitir que el indicador de posición pueda manejar la situación descrita al inicio de este tema. Veamos qué cambió aquí en este código respecto a lo que se hacía anteriormente.

Básicamente, en la función de la línea 61, ahora ya no creamos los objetos HLINE. Aquellas líneas que hacían esto fueron eliminadas. En realidad, no fueron eliminadas, sino reubicadas. Observa que todo el código sigue igual. Sin embargo, al ir a la línea 19, podremos ver algo muy extraño que antes no ocurría.

Observa lo siguiente: antes, cuando pedíamos mover una línea de precio, el código solo indicaba al objeto que se moviera. Pero ahora hay un paso adicional. Este paso se encuentra en la línea 25. El hecho de que este paso exista nos permite recrear la línea cuando sea necesario. Es decir, cuando el operador elimine la línea, el Asesor Experto le dirá al indicador de posición que actualice los datos. El indicador eliminará la misma línea que el operador haya eliminado. Sin embargo, cuando el operador reubique la línea, indicando que ahora tendremos un precio de stop loss o take profit, el Asesor Experto le dirá al indicador de posición que vuelva a actualizar los datos. En este punto, cuando se ejecute la línea 25, el indicador de posición notará que el objeto HLINE no existe y lo creará de forma adecuada al nuevo estado de la posición. Solo después, el indicador de posición posicionará la línea en el lugar adecuado. Esto se hace usando la línea 31.

Lo que acabo de explicar puede verse en el siguiente video, para dejar claro cómo funciona este indicador de posición actualmente.


Consideraciones finales

En este artículo, hicimos diversas mejoras para hacer que el indicador de posición refleje lo que realmente ocurre en el servidor de trading, en términos de posiciones y del estado actual de estas. Debo recordar que estas aplicaciones, mostradas aquí, no pretenden en ningún caso sustituir ningún elemento disponible en MetaTrader 5. Tampoco deben usarse sin los debidos cuidados y criterios, ya que su objetivo es presentar un código didáctico, es decir, con fines de aprendizaje sobre cómo funcionan las cosas.

El motivo por el que digo que el código es didáctico es que el uso de mensajes, en algunos casos, no es la mejor forma de implementar ciertas funcionalidades. Observa que el indicador de posición, al colocarse en el gráfico, crea un mensaje que él mismo responderá. Esta característica reduce un poco la agilidad que el código podría alcanzar, ya que estamos añadiendo una llamada donde no sería necesaria.

En cualquier caso, no pretendo cambiar la manera en que se presenta el código. Si deseas probar este código, pero no sabes cómo crear todos los archivos, aunque aquí se presenta todo el código íntegramente, en el anexo tendrás las aplicaciones ya compiladas. Así, bastará con usarlas en MetaTrader 5 y ver cómo funciona el intercambio de mensajes. Estas aplicaciones fueron probadas en cuentas de tipo HEDGING y NETTING, y funcionan perfectamente. Basta con que no cambies nada. Solo descomprime el anexo en el directorio de MQL5. Obtendrás el mismo resultado que se ve en el video.

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 interactuar 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 mostrar las órdenes de mercado, y permite interactuar con ellas y controlarlas.
Indicators\Position View.mq5Responsable de mostrar las posiciones de mercado, lo que permite interactuar con ellas y controlarlas.
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/13203

Archivos adjuntos |
Anexo.zip (779.24 KB)
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.
Redes neuronales en el trading: Previsión probabilística de series temporales (Final) Redes neuronales en el trading: Previsión probabilística de series temporales (Final)
Le invitamos a explorar el framework K²VAE y a descubrir cómo integrar los enfoques propuestos en su sistema de negociación. Hoy aprenderá cómo el enfoque híbrido Koopman-Kalman-VAE ayuda a construir modelos adaptativos e interpretables. Al final del artículo le presentaremos los resultados prácticos del uso de las soluciones implementadas.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
De novato a experto: Noticias animadas utilizando MQL5 (V) Sistema de recordatorio de eventos De novato a experto: Noticias animadas utilizando MQL5 (V) Sistema de recordatorio de eventos
En esta discusión, exploraremos nuevas mejoras a medida que integramos una lógica mejorada de alertas de eventos para los acontecimientos del calendario económico que muestra el EA «News Headline». Esta mejora es fundamental, ya que garantiza que los usuarios reciban notificaciones oportunas poco antes de que tengan lugar eventos importantes. Acompáñanos en este análisis para descubrir más.