Português
preview
Desarrollo de un sistema de repetición (Parte 49): Esto complica las cosas (I)

Desarrollo de un sistema de repetición (Parte 49): Esto complica las cosas (I)

MetaTrader 5Ejemplos | 19 julio 2024, 09:43
18 0
Daniel Jose
Daniel Jose

Introducción

En este artículo utilizaremos lo que vimos en el artículo Desarrollo de un sistema de repetición (Parte 48): Conceptos que hay que entender y comprender Entonces, si no leíste ese artículo, léelo, pues el contenido allí descrito será muy importante para entender lo que haremos aquí.

Una de las cosas que más me incomodaba durante los artículos anteriores era el hecho de que el sistema de repetición/simulación contenía un indicador que era visible para el usuario de MetaTrader 5 en el área donde se enumeran los indicadores para poder colocarlos en el gráfico.

A pesar de contar con un bloqueo que se implementó durante los artículos para impedir que el usuario intentara colocar tal indicador en el gráfico incorrecto, es decir, un gráfico diferente al utilizado por el servicio de repetición/simulación. Aun así, el hecho de que tal indicador estuviera presente entre los otros me incomodaba mucho.

Durante todos estos meses, pasé intentando y analizando cómo podría hacer las cosas de la manera que, en mi opinión, parecía ser la más adecuada. Afortunadamente, recientemente logré encontrar la solución para mejorar las cosas. Con esto, el indicador de control dejará de estar presente entre los demás indicadores y pasará a ser parte integral del servicio de repetición/simulación.

Al hacer esto, tendremos un mayor grado de libertad en relación con algunos factores. Sin embargo, haré los cambios poco a poco, ya que también modificaré el indicador con el fin de reducir la carga de MetaTrader 5. En otras palabras, dejaremos de utilizar algunos recursos y pasaremos a usar otros de la plataforma. Esto traerá una mejor estabilidad, seguridad y confiabilidad al sistema de repetición/simulación.

Veamos entonces cómo se llevará a cabo, ya que los cambios serán muy interesantes de implementar y nos aportarán un gran conocimiento sobre cómo trabajar de forma más avanzada en MQL5. Hay que recordar que esto no supone un retraso en el desarrollo del sistema de repetición/simulación. De hecho, el sistema necesita algunas cosas que estudiaremos en este artículo, con el fin de codificar otras cosas en el futuro.

Así que continuemos nuestra saga hacia una implementación más avanzada del servicio de repetición/simulación.


Inicio de los cambios

Para dejar claro lo que se va a hacer, iré con calma, así tú, querido lector, podrás seguir los cambios que de hecho sucederán, y necesitan suceder. Muchos pueden pensar que la forma en que mostraré las cosas aquí es totalmente innecesaria. Que podríamos pasar directamente al código ya finalizado y listo. Pero si esto fuera lo que debería hacerse, entonces, ¿por qué existen los artículos anteriores? No tiene sentido, ¿verdad? Pero como aquí el material está enfocado en motivar e incentivar a las personas a producir sus propios programas y soluciones, debo mostrar cómo hacer para limpiar un código ya listo y convertirlo en adecuado para su uso en un modelo más elaborado. Esto, sin que se vuelva algo extraño y deje de funcionar.

Entonces, ten calma. Vamos a seguir las cosas siempre pensando en quienes tienen menos experiencia.

Para empezar, eliminaremos el sistema de vinculación del ID del gráfico. Este sistema evita que el indicador de control pueda ser colocado en más de un gráfico. Al mismo tiempo, evita que se coloque en el gráfico incorrecto. Aunque esta eliminación se haga aquí, luego volveremos a poner este mismo sistema en funcionamiento. Sin embargo, cuando se haga, será de manera que el indicador se mantenga en el gráfico correcto. Pero este trabajo lo realizará el servicio de repetición/simulación, y no más el indicador.

Para que esta eliminación se haga de manera adecuada y segura, usaremos siempre el mismo punto común entre el servicio y el indicador de control. Este código común se observa a continuación:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay               "RePlay"
05. #define def_GlobalVariableReplay       def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableIdGraphics   def_SymbolReplay + "_ID"
07. #define def_GlobalVariableServerTime   def_SymbolReplay + "_Time"
08. #define def_MaxPosSlider               400
09. //+------------------------------------------------------------------+
10. union u_Interprocess
11. {
12.     union u_0
13.     {
14.             double  df_Value;       // Value of the terminal global variable...
15.             ulong   IdGraphic;      // Contains the Graph ID of the asset...
16.     }u_Value;
17.     struct st_0
18.     {
19.             bool    isPlay;         // Indicates whether we are in Play or Pause mode...
20.             bool    isWait;         // Tells the user to wait...
21.             bool    isHedging;      // If true we are in a Hedging account, if false the account is Netting...
22.             bool    isSync;         // If true indicates that the service is synchronized...
23.             ushort  iPosShift;      // Value between 0 and 400...
24.     }s_Infos;
25.     datetime        ServerTime;
26. };
27. //+------------------------------------------------------------------+
28. union uCast_Double
29. {
30.     double   dValue;
31.     long     _long;                  // 1 Information
32.     datetime _datetime;              // 1 Information
33.     int      _int[sizeof(double)];   // 2 Informations
34.     char     _char[sizeof(double)];  // 8 Informations
35. };
36. //+------------------------------------------------------------------+

Código fuente Interprocess.mqh

Si observas en la línea 06, tenemos una definición. Esta indica tanto para el servicio como para el indicador cuál es el nombre de la variable global del terminal que se usará para pasar el ID del gráfico. Si se elimina esta línea al intentar compilar el indicador y el servicio, se generará una serie de errores. Y es precisamente esto lo que queremos. De hecho, cada uno de los puntos de error que se genere en la compilación indica dónde deberemos actuar para eliminar esta comunicación que existe entre el servicio y el indicador, a través de la variable global del terminal.

Esto, para que no sea algo extremadamente agotador y repetitivo, y como haremos diversos cambios, mostraré las cosas en términos de fragmentos de código. Pero siempre indicando exactamente dónde debes actuar en el código original para mantener el sistema en funcionamiento.

Así que, comencemos viendo el siguiente hecho.

01. //+------------------------------------------------------------------+
02. #property service
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property copyright "Daniel Jose"
05. #property version   "1.49"
06. #property description "Replay-Simulator service for MT5 platform."
07. #property description "This is dependent on the Market Replay indicator."
08. #property description "For more details on this version see the article."
09. #property link "https://www.mql5.com/es/articles/11820"
10. //+------------------------------------------------------------------+
11. #define def_Dependence_01   "Indicators\\Replay\\Market Replay.ex5"
12. #resource "\\" + def_Dependence_01
13. //+------------------------------------------------------------------+
14. #include <Market Replay\Service Graphics\C_Replay.mqh>
15. //+------------------------------------------------------------------+
16. input string           user00 = "Forex - EURUSD.txt";  //Replay configuration file.
17. input ENUM_TIMEFRAMES  user01 = PERIOD_M5;             //Initial graphic time.
18. //+------------------------------------------------------------------+
19. void OnStart()
20. {
21.     C_Replay  *pReplay;
22. 
23.     pReplay = new C_Replay(user00);
24.     if ((*pReplay).ViewReplay(user01))
25.     {
26.             Print("Permission granted. Replay service can now be used...");
27.             while ((*pReplay).LoopEventOnTime(false));
28.     }
29.     delete pReplay;
30. }
31. //+------------------------------------------------------------------+

Código fuente: Servicio replay.mq5

En este código, que se muestra arriba y que es precisamente el código del servicio, puedes ver que en las líneas 11 y 12 tenemos algo un poco diferente de lo que existía en los códigos anteriores. La diferencia es sutil, pero existe. Sin embargo, es justamente esta diferencia la que promoverá lo que queremos y haremos de hecho. Luego volveremos a hablar más sobre esto. Pero la cuestión aquí es la siguiente: Si el indicador de control se compiló o no recientemente, después de que modificamos el código de cabecera eliminando la definición del nombre de la variable global del terminal.

Y sí, en el momento en que escribo este artículo, todavía no contamos con un sistema de compilación MAKE en MetaEditor. Pero quién sabe si en el futuro, quienes mantienen la plataforma y el lenguaje MQL5, lleguen a implementar tal cosa para nosotros. Pero hasta entonces debemos tomar ciertos cuidados.

Antes de continuar con la explicación, permíteme hacer una breve pausa para explicar qué es este sistema de compilación MAKE. Esto puede resultar novedoso para muchos, pero, para quienes ya programan profesionalmente desde hace años, este sistema es muy conocido. Básicamente es lo siguiente: Creas tu código fuente normalmente. Pero a veces será necesario crear varios archivos ejecutables al mismo tiempo. También puede aprovecharse para generar un único ejecutable, aunque no es tan necesario hacerlo a través del MAKE.

Entonces, antes de compilar las cosas de hecho, creas otro archivo. Este es el archivo MakeFile, que contiene una serie de pasos, definiciones, configuraciones y ajustes necesarios para que el compilador y el LinkEditor generen todos los archivos ejecutables. Y esto en un solo paso. La gran ventaja de este sistema es que, si modificas un solo archivo, ya sea de cabecera o de biblioteca, MAKE lo detectará y realizará una compilación solo de los archivos realmente necesarios. Así, al final tendrás todos los ejecutables actualizados según sea necesario.

Este tipo de cosa quita del programador la responsabilidad de saber qué debe o no ser actualizado al momento de crear el ejecutable final, esa responsabilidad recae en el MAKE. Así, no corres el riesgo de crear algo, o corregir una falla, que sea corregida en un ejecutable y permanezca en otro. Esto porque el MAKE hace todo el trabajo por nosotros.

Sin embargo, hasta el momento en que se escribe este artículo, dicha herramienta aún no existe en MetaEditor. Es posible crear algo similar mediante archivos por lotes desde la línea de comandos, pero sería solo una chapuza y no una solución ideal. Así que no entraré en detalles sobre cómo se hace. Pero como en la línea 12 del código anterior se indica al compilador MQL5 que el ejecutable del indicador de control debe añadirse al servicio, puedes pedir simplemente que se compilen el código del servicio y el código del indicador. Ya hablé sobre esto anteriormente. Pero vuelvo a resaltar algo aquí nuevamente: Para que el indicador se compile adecuadamente, ya con las actualizaciones, cada vez que compiles el código del servicio, deberás eliminar el ejecutable del indicador. Si no haces esto, el código del indicador no se compilará nuevamente cuando compiles el código del servicio. Así que presta atención a esto.

Entonces, al intentar compilar el código del servicio, obtendrás el siguiente resultado, que puede verse en la imagen 01.

Figura 01

Imagen 01 - Resultado del intento de compilar el nuevo código.

Puedes notar que tenemos 2 errores y una advertencia. La advertencia no es, y no debe ser tu primera preocupación. Primero debes corregir los errores. Si observas la imagen 01, podrás ver dónde están. Solo tienes que hacer clic en ellos y se te dirigirá al lugar exacto donde ocurrieron.

El error indicado en la imagen 01, en la línea 12, se debe a que el compilador no pudo encontrar el ejecutable para colocarlo como un recurso del servicio.

Sin embargo, el error señalado en la línea 156 sí forma parte del código del servicio y debemos corregirlo para poder progresar en la compilación.

El código donde se encuentra el error puede verse en el siguiente fragmento. 

141. ~C_Replay()
142.    {
143.            ArrayFree(m_Ticks.Info);
144.            ArrayFree(m_Ticks.Rate);
145.            m_IdReplay = ChartFirst();
146.            do
147.            {
148.                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                            ChartClose(m_IdReplay);
150.            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.            CustomSymbolDelete(def_SymbolReplay);
155.            GlobalVariableDel(def_GlobalVariableReplay);
156.            GlobalVariableDel(def_GlobalVariableIdGraphics);
157.            GlobalVariableDel(def_GlobalVariableServerTime);
158.            Print("Finished replay service...");
159.    }

Fragmento del código C_Replay.mqh

Observa que puedes comentar o simplemente eliminar la línea 156 del código. Dado que el cambio será definitivo, eliminemos esta línea en este momento. Así, el servicio ya no verá esta variable global del terminal. Pero, incluso corrigiendo el servicio, al intentar volver a compilar el código, se obtiene el resultado del intento en la imagen 02.

Figura 02

Imagen 02 - Nuevo intento de compilación.

Ahora solo tenemos la indicación de un error y no hay más alertas. En este momento, muchos lectores con menos experiencia se preguntan cómo corregir el error mostrado. Sin embargo, olvidan leer lo que el compilador está informando.

Presta atención a la imagen 02. Observa que justo encima del mensaje de error, se están proporcionando algunas otras informaciones. NUNCA debes ignorar estos detalles del compilador. Debes prestar atención a todos ellos. Literalmente a TODOS. Y en el mensaje inmediatamente anterior al error puedes ver la siguiente información del compilador:

compiling '\Indicators\Replay\Market Replay.mq5' failed

Este mensaje es clave, ya que nos indica que el compilador, por alguna razón, no está consiguiendo crear el ejecutable del indicador. Entonces, dado que el código del servicio se está compilando parcialmente, debemos centrar nuestra atención en el código del indicador. Abrimos dicho código en MetaEditor y solicitamos al compilador que intente generar el ejecutable. Y antes de que alguien piense: ¿Pero por qué estás haciendo esto? ¿Acaso no sabes que el código contiene errores? Sí, sabemos que contiene errores. Pero queremos que el compilador nos diga dónde. No es productivo buscar los errores manualmente. Deja que el compilador te los indique.

Entonces, al abrir el código del indicador y pedir al compilador que genere el ejecutable, verás la imagen 03.

Figura 03

Imagen 03 - Intento de compilar el indicador de control.

Una vez más, tenemos la indicación de cinco errores y cinco alertas. Primero, ocúpate de los errores; luego, de las alertas. Al hacer clic primero en el primer error informado, seremos dirigidos al código, en la ubicación exacta del error. Un detalle: Mucha gente piensa que debes hacer clic en los errores de manera aleatoria. Pero, por regla general, debes proceder de la siguiente manera: Busca el primer error en la lista. No importan los demás, ve siempre al primero. Haz la corrección necesaria. E intenta compilar el código nuevamente. Haz esto hasta que el código se compile sin alertas ni errores. Es muy común, especialmente para los principiantes, volverse completamente locos cuando al intentar compilar un código, este genera a veces 100 o más errores.

Sin embargo, en muchas ocasiones, al corregir el primer error o el que causó el primer error, todo el código se compila al final. Entonces, sigue este consejo: Busca el primer error que te indique el compilador. Ve cómo corregirlo adecuadamente, intenta compilar nuevamente el código, si surge un nuevo error, ve al primero listado y haz la corrección, y así hasta que no haya más errores. Lo mismo debe hacerse en el caso de las advertencias. Ve siempre al primero de la lista.

Dado que todos los errores informados por el compilador y vistos en la imagen 03 están en el mismo código, es más sencillo colocar todo el código para que sepas dónde estarán las modificaciones. 

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/es/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. #define def_BitShift ((sizeof(ulong) * 8) - 1)
14. //+------------------------------------------------------------------+
15. C_Terminal *terminal = NULL;
16. C_Controls *control = NULL;
17. //+------------------------------------------------------------------+
18. #define def_InfoTerminal (*terminal).GetInfoTerminal()
19. #define def_ShortName       "Market_" + def_SymbolReplay
20. //+------------------------------------------------------------------+
21. int OnInit()
22. {
23. #define macro_INIT_FAILED { ChartIndicatorDelete(def_InfoTerminal.ID, 0, def_ShortName); return INIT_FAILED; }
24.     u_Interprocess Info;
25.     ulong ul = 1;
26. 
27.     ResetLastError();
28.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
29.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
30.     ul <<= def_BitShift;
31.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
32.     if ((def_InfoTerminal.szSymbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
33.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
34.     if (Info.u_Value.IdGraphic != def_InfoTerminal.ID) macro_INIT_FAILED;
35.     if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
36.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
37.     Info.u_Value.IdGraphic |= ul;
38.     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
39.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
40.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
41.     (*control).Init(Info.s_Infos.isPlay);
42.         
43.     return INIT_SUCCEEDED;
44.     
45. #undef macro_INIT_FAILED
46. }
47. //+------------------------------------------------------------------+
48. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
49. {
50.     static bool bWait = false;
51.     u_Interprocess Info;
52.     
53.     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
54.     if (!bWait)
55.     {
56.             if (Info.s_Infos.isWait)
57.             {
58.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
59.                     bWait = true;
60.             }
61.     }else if (!Info.s_Infos.isWait)
62.     {
63.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.u_Value.df_Value, "");
64.             bWait = false;
65.     }
66.     
67.     return rates_total;
68. }
69. //+------------------------------------------------------------------+
70. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
71. {
72.     (*control).DispatchMessage(id, lparam, dparam, sparam);
73. }
74. //+------------------------------------------------------------------+
75. void OnDeinit(const int reason)
76. {
77.     u_Interprocess Info;
78.     ulong ul = 1;
79. 
80.     switch (reason)
81.     {
82.             case REASON_CHARTCHANGE:
83.                     ul <<= def_BitShift;
84.                     Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
85.                     Info.u_Value.IdGraphic ^= ul;
86.                     GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
87.                     break;
88.             case REASON_REMOVE:
89.             case REASON_CHARTCLOSE:
90.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
91.                     GlobalVariableDel(def_GlobalVariableReplay);
92.                     ChartClose(def_InfoTerminal.ID);
93.                     break;
94.     }
95.     delete control;
96.     delete terminal;
97. }
98. //+------------------------------------------------------------------+

Código fuente del indicador: Market replay.mq5

Todas las líneas que están resaltadas dejaron de existir en el código. Así, al intentar compilarlo nuevamente recibirás el siguiente mensaje visto en la imagen 04.

Figura 04

Imagen 04

Observa que en esta imagen 04 hay una alerta. Esta, de hecho, no interferirá en el código, pero queda ahí molestando. Por lo tanto, ve a la línea 77 y elimina esa línea e intenta compilar nuevamente el código. Recibirás el mensaje visto en la imagen 05, que es justamente el que queremos. Pero atención, la línea 77 solo puede ser eliminada porque el compilador nos informó que la variable presente allí no está siendo utilizada.

Figura 05

Imagen 05 - Compilación realizada con éxito.

Muy bien, muy bien, muy bien. Nuestro código se limpió parcialmente. Sin embargo, volviendo al código del archivo de encabezado Interprocess.mqh, visto al inicio de este tema, puedes observar que aún hay algo presente en el código que ya no será necesario. Esto se debe a que ya no estamos utilizando la variable global de terminal para informar al indicador de cuál es el ID del gráfico. Entonces, la línea 15 del archivo Interprocess.mqh deberá ser eliminada. Pero surge la pregunta: "¿por qué no había eliminado esta línea antes?" El motivo es que se necesita tener un cuidado especial en el archivo de servicio. Pero hay otro motivo también, y para entenderlo, pasemos al siguiente tema.


Radicalización de decisiones

Cuando se diseñó el servicio de repetición/simulador, esto era así. Una de las cosas que intenté hacer fue justamente evitar que el usuario interfiera en lo que debería o no estar presente en el gráfico.

La forma que encontré para resolver este problema fue agregar una variable global de terminal para informar al indicador en qué gráfico debería estar presente. No podría colocarlo el usuario en otro gráfico. De hecho, esta solución fue adecuada y bastante interesante de implementar. Puedes usar algo parecido cuando desees asegurar que un indicador, script o EA no sea colocado en un gráfico, pero esto no viene al caso ahora.

En el momento en que utilizamos el servicio para controlar lo que deberá o no estar en el gráfico, dicho sistema se torna completamente innecesario. O mejor dicho: Ya que el indicador estará presente como un recurso del servicio, y no estará accesible al usuario. No tiene sentido continuar manteniendo la codificación de la forma en que se estaba haciendo. El nivel de seguridad ha aumentado considerablemente y el control de acceso ha cambiado por completo.

Por lo tanto, ya que al eliminar la variable ID del sistema, debemos hacerlo con calma. Por este motivo, no hice la eliminación en el tema anterior. Pronto comprenderás la razón. Pues si la eliminación se realizara antes de tiempo, sería muy difícil hacer los cambios adecuados y cometeríamos muchos errores.

El mayor triunfo de un gran programador es precisamente este. Tener CALMA. Abordar el problema de uno en uno, corrigiendo y modificando las cosas de manera que se mantenga lo que antes era posible hacer. Y, si es necesario o interesante, ampliar las capacidades del programa. Entonces el lema es: 

Dividir para conquistar.


Veamos cómo se limpiará el código. De esta forma, el nuevo código del archivo Interprocess.mqh se puede ver íntegramente a continuación:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_SymbolReplay             "RePlay"
05. #define def_GlobalVariableReplay     def_SymbolReplay + "_Infos"
06. #define def_GlobalVariableServerTime def_SymbolReplay + "_Time"
07. #define def_MaxPosSlider             400
08. //+------------------------------------------------------------------+
09. union u_Interprocess
10. {
11.     double  df_Value; // Value of the terminal global variable...
12.     struct st_0
13.     {
14.             bool    isPlay;     // Indicates whether we are in Play or Pause mode...
15.             bool    isWait;     // Tells the user to wait...
16.             bool    isHedging;  // If true we are in a Hedging account, if false the account is Netting...
17.             bool    isSync;     // If true indicates that the service is synchronized...
18.             ushort  iPosShift;  // Value between 0 and 400...
19.     }s_Infos;
20.     datetime ServerTime;
21. };
22. //+------------------------------------------------------------------+
23. union uCast_Double
24. {
25.     double  dValue;
26.     long     _long;                  // 1 Information
27.     datetime _datetime;              // 1 Information
28.     int      _int[sizeof(double)];   // 2 Informations
29.     char     _char[sizeof(double)];  // 8 Informations
30. };
31. //+------------------------------------------------------------------+

Código fuente: Interprocess.mqh

Observen que, aunque este código no parece tan diferente, marcará una gran diferencia en el sistema que acabamos de modificar en el tema anterior. Entonces, al intentar compilar nuevamente el sistema, no te sorprendas ni te asustes por la cantidad de errores que se generarán. Todo lo que necesitaremos hacer será reajustar las cosas para que podamos tener el funcionamiento que teníamos antes. Pero ahora el indicador de control dejará de ser accesible al usuario. El servicio será el responsable de colocarlo y mantenerlo en el gráfico correcto.

Al intentar compilar el código del servicio, se obtendrá el siguiente retorno del compilador. Este puede verse en la imagen 06.

Figura 06

Imagen 06 - Muchas fallas. Pero, ¿las tenemos todas?

Observa que la cantidad de errores fue bastante grande, además de la cantidad de alertas. Como mencioné anteriormente, comenzaremos con el primer error en esta lista. Por lo tanto, al empezar a hacer los cambios, comenzarán en la línea 28 del archivo de encabezado C_Replay.mqh. Para que no resulte una explicación demasiado agobiante, observemos el código a continuación, ya que la mayor parte de lo que necesitaremos hacer será eliminar u_Value del código, por lo que el código ya sin esta referencia puede verse íntegramente a continuación:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_ConfigService.mqh"
005. //+------------------------------------------------------------------+
006. class C_Replay : private C_ConfigService
007. {
008.    private :
009.            long   m_IdReplay;
010.            struct st01
011.            {
012.                   MqlRates Rate[1];
013.                   datetime memDT;
014.            }m_MountBar;
015.            struct st02
016.            {
017.                   bool    bInit;
018.                   double  PointsPerTick;
019.                   MqlTick tick[1];
020.            }m_Infos;
021. //+------------------------------------------------------------------+
022.            void AdjustPositionToReplay(const bool bViewBuider)
023.                    {
024.                            u_Interprocess Info;
025.                            MqlRates       Rate[def_BarsDiary];
026.                            int            iPos, nCount;
027.                            
028.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
029.                            if (Info.s_Infos.iPosShift == (int)((m_ReplayCount * def_MaxPosSlider * 1.0) / m_Ticks.nTicks)) return;
030.                            iPos = (int)(m_Ticks.nTicks * ((Info.s_Infos.iPosShift * 1.0) / (def_MaxPosSlider + 1)));
031.                            Rate[0].time = macroRemoveSec(m_Ticks.Info[iPos].time);
032.                            CreateBarInReplay(true);
033.                            if (bViewBuider)
034.                            {
035.                                    Info.s_Infos.isWait = true;
036.                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
037.                            }else
038.                            {
039.                                    for(; Rate[0].time > (m_Ticks.Info[m_ReplayCount].time); m_ReplayCount++);
040.                                    for (nCount = 0; m_Ticks.Rate[nCount].time < macroRemoveSec(m_Ticks.Info[iPos].time); nCount++);
041.                                    nCount = CustomRatesUpdate(def_SymbolReplay, m_Ticks.Rate, nCount);
042.                            }
043.                            for (iPos = (iPos > 0 ? iPos - 1 : 0); (m_ReplayCount < iPos) && (!_StopFlag);) CreateBarInReplay(false);
044.                            CustomTicksAdd(def_SymbolReplay, m_Ticks.Info, m_ReplayCount);
045.                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
046.                            Info.s_Infos.isWait = false;
047.                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
048.                    }
049. //+------------------------------------------------------------------+
050. inline void CreateBarInReplay(const bool bViewTicks)
051.                    {
052. #define def_Rate m_MountBar.Rate[0]
053. 
054.                            bool    bNew;
055.                            double  dSpread;
056.                            int     iRand = rand();
057.                            
058.                            if (BuildBar1Min(m_ReplayCount, def_Rate, bNew))
059.                            {
060.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
061.                                    if ((!m_Ticks.bTickReal) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
062.                                    {                                               
063.                                            dSpread = m_Infos.PointsPerTick + ((iRand > 29080) && (iRand < 32767) ? ((iRand & 1) == 1 ? m_Infos.PointsPerTick : 0 ) : 0 );
064.                                            if (m_Infos.tick[0].last > m_Infos.tick[0].ask)
065.                                            {
066.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last;
067.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last - dSpread;
068.                                            }else   if (m_Infos.tick[0].last < m_Infos.tick[0].bid)
069.                                            {
070.                                                    m_Infos.tick[0].ask = m_Infos.tick[0].last + dSpread;
071.                                                    m_Infos.tick[0].bid = m_Infos.tick[0].last;
072.                                            }
073.                                    }
074.                                    if (bViewTicks) CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
075.                                    CustomRatesUpdate(def_SymbolReplay, m_MountBar.Rate);
076.                            }
077.                            m_ReplayCount++;
078. #undef def_Rate
079.                    }
080. //+------------------------------------------------------------------+
081.            void ViewInfos(void)
082.                    {
083.                            MqlRates Rate[1];
084.                            
085.                            ChartSetInteger(m_IdReplay, CHART_SHOW_ASK_LINE, m_Ticks.ModePlot == PRICE_FOREX);
086.                            ChartSetInteger(m_IdReplay, CHART_SHOW_BID_LINE, m_Ticks.ModePlot == PRICE_FOREX);
087.                            ChartSetInteger(m_IdReplay, CHART_SHOW_LAST_LINE, m_Ticks.ModePlot == PRICE_EXCHANGE);
088.                            m_Infos.PointsPerTick = SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE);
089.                            m_MountBar.Rate[0].time = 0;
090.                            m_Infos.bInit = true;
091.                            CopyRates(def_SymbolReplay, PERIOD_M1, 0, 1, Rate);
092.                            if ((m_ReplayCount == 0) && (m_Ticks.ModePlot == PRICE_EXCHANGE))
093.                                    for (; m_Ticks.Info[m_ReplayCount].volume_real == 0; m_ReplayCount++);
094.                            if (Rate[0].close > 0)
095.                            {
096.                                    if (m_Ticks.ModePlot == PRICE_EXCHANGE) m_Infos.tick[0].last = Rate[0].close; else
097.                                    {
098.                                            m_Infos.tick[0].bid = Rate[0].close;
099.                                            m_Infos.tick[0].ask = Rate[0].close + (Rate[0].spread * m_Infos.PointsPerTick);
100.                                    }                                       
101.                                    m_Infos.tick[0].time = Rate[0].time;
102.                                    m_Infos.tick[0].time_msc = Rate[0].time * 1000;
103.                            }else
104.                                    m_Infos.tick[0] = m_Ticks.Info[m_ReplayCount];
105.                            CustomTicksAdd(def_SymbolReplay, m_Infos.tick);
106.                            ChartRedraw(m_IdReplay);
107.                    }
108. //+------------------------------------------------------------------+
109.            void CreateGlobalVariable(const string szName, const double value)
110.                    {
111.                            GlobalVariableDel(szName);
112.                            GlobalVariableTemp(szName);     
113.                            GlobalVariableSet(szName, value);
114.                    }
115. //+------------------------------------------------------------------+
116.    public  :
117. //+------------------------------------------------------------------+
118.            C_Replay(const string szFileConfig)
119.                    {
120.                            m_ReplayCount = 0;
121.                            m_dtPrevLoading = 0;
122.                            m_Ticks.nTicks = 0;
123.                            m_Infos.bInit = false;
124.                            Print("************** Market Replay Service **************");
125.                            srand(GetTickCount());
126.                            GlobalVariableDel(def_GlobalVariableReplay);
127.                            SymbolSelect(def_SymbolReplay, false);
128.                            CustomSymbolDelete(def_SymbolReplay);
129.                            CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay), _Symbol);
130.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
131.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
132.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0);
133.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 0);
134.                            CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP, 0);
135.                            CustomSymbolSetString(def_SymbolReplay, SYMBOL_DESCRIPTION, "Symbol for replay / simulation");
136.                            CustomSymbolSetInteger(def_SymbolReplay, SYMBOL_DIGITS, 8);
137.                            m_IdReplay = (SetSymbolReplay(szFileConfig) ? 0 : -1);
138.                            SymbolSelect(def_SymbolReplay, true);
139.                    }
140. //+------------------------------------------------------------------+
141.            ~C_Replay()
142.                    {
143.                            ArrayFree(m_Ticks.Info);
144.                            ArrayFree(m_Ticks.Rate);
145.                            m_IdReplay = ChartFirst();
146.                            do
147.                            {
148.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
149.                                            ChartClose(m_IdReplay);
150.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
151.                            for (int c0 = 0; (c0 < 2) && (!SymbolSelect(def_SymbolReplay, false)); c0++);
152.                            CustomRatesDelete(def_SymbolReplay, 0, LONG_MAX);
153.                            CustomTicksDelete(def_SymbolReplay, 0, LONG_MAX);
154.                            CustomSymbolDelete(def_SymbolReplay);
155.                            GlobalVariableDel(def_GlobalVariableReplay);
156.                            GlobalVariableDel(def_GlobalVariableServerTime);
157.                            Print("Finished replay service...");
158.                    }
159. //+------------------------------------------------------------------+
160.            bool ViewReplay(ENUM_TIMEFRAMES arg1)
161.                    {
162. #define macroError(A) { Print(A); return false; }
163.                            u_Interprocess info;
164.                            
165.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE) == 0)
166.                                    macroError("Asset configuration is not complete, it remains to declare the size of the ticket.");
167.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE) == 0)
168.                                    macroError("Asset configuration is not complete, need to declare the ticket value.");
169.                            if (SymbolInfoDouble(def_SymbolReplay, SYMBOL_VOLUME_STEP) == 0)
170.                                    macroError("Asset configuration not complete, need to declare the minimum volume.");
171.                            if (m_IdReplay == -1) return false;
172.                            if ((m_IdReplay = ChartFirst()) > 0) do
173.                            {
174.                                    if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
175.                                    {
176.                                            ChartClose(m_IdReplay);
177.                                            ChartRedraw();
178.                                    }
179.                            }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
180.                            Print("Waiting for [Market Replay] indicator permission to start replay ...");
181.                            info.ServerTime = ULONG_MAX;
182.                            CreateGlobalVariable(def_GlobalVariableServerTime, info.df_Value);
183.                            m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
184.                            ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
185.                            while ((!GlobalVariableGet(def_GlobalVariableReplay, info.df_Value)) && (!_StopFlag) && (ChartSymbol(m_IdReplay) != "")) Sleep(750);
186.                            info.s_Infos.isHedging = TypeAccountIsHedging();
187.                            info.s_Infos.isSync = true;
188.                            GlobalVariableSet(def_GlobalVariableReplay, info.df_Value);
189. 
190.                            return ((!_StopFlag) && (ChartSymbol(m_IdReplay) != ""));
191. #undef macroError
192.                    }
193. //+------------------------------------------------------------------+
194.            bool LoopEventOnTime(const bool bViewBuider)
195.                    {
196.                            u_Interprocess Info;
197.                            int iPos, iTest, iCount;
198.                            
199.                            if (!m_Infos.bInit) ViewInfos();
200.                            iTest = 0;
201.                            while ((iTest == 0) && (!_StopFlag))
202.                            {
203.                                    iTest = (ChartSymbol(m_IdReplay) != "" ? iTest : -1);
204.                                    iTest = (GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value) ? iTest : -1);
205.                                    iTest = (iTest == 0 ? (Info.s_Infos.isPlay ? 1 : iTest) : iTest);
206.                                    if (iTest == 0) Sleep(100);
207.                            }
208.                            if ((iTest < 0) || (_StopFlag)) return false;
209.                            AdjustPositionToReplay(bViewBuider);
210.                            Info.ServerTime = m_Ticks.Info[m_ReplayCount].time;
211.                            GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
212.                            iPos = iCount = 0;
213.                            while ((m_ReplayCount < m_Ticks.nTicks) && (!_StopFlag))
214.                            {
215.                                    iPos += (int)(m_ReplayCount < (m_Ticks.nTicks - 1) ? m_Ticks.Info[m_ReplayCount + 1].time_msc - m_Ticks.Info[m_ReplayCount].time_msc : 0);
216.                                    CreateBarInReplay(true);
217.                                    while ((iPos > 200) && (!_StopFlag))
218.                                    {
219.                                            if (ChartSymbol(m_IdReplay) == "") return false;
220.                                            GlobalVariableGet(def_GlobalVariableReplay, Info.df_Value);
221.                                            if (!Info.s_Infos.isPlay) return true;
222.                                            Info.s_Infos.iPosShift = (ushort)((m_ReplayCount * def_MaxPosSlider) / m_Ticks.nTicks);
223.                                            GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
224.                                            Sleep(195);
225.                                            iPos -= 200;
226.                                            iCount++;
227.                                            if (iCount > 4)
228.                                            {
229.                                                    iCount = 0;
230.                                                    GlobalVariableGet(def_GlobalVariableServerTime, Info.df_Value);
231.                                                    if ((m_Ticks.Info[m_ReplayCount].time - m_Ticks.Info[m_ReplayCount - 1].time) > 60) Info.ServerTime = ULONG_MAX; else
232.                                                    {
233.                                                            Info.ServerTime += 1;
234.                                                            Info.ServerTime = ((Info.ServerTime + 1) < m_Ticks.Info[m_ReplayCount].time ? Info.ServerTime : m_Ticks.Info[m_ReplayCount].time);
235.                                                    };
236.                                                    GlobalVariableSet(def_GlobalVariableServerTime, Info.df_Value);
237.                                            }
238.                                    }
239.                            }                               
240.                            return (m_ReplayCount == m_Ticks.nTicks);
241.                    }                               
242. //+------------------------------------------------------------------+
243. };
244. //+------------------------------------------------------------------+
245. #undef macroRemoveSec
246. #undef def_SymbolReplay
247. //+------------------------------------------------------------------+

Código fuente del archivo C_Replay.mqh

Este código, que se muestra arriba, ya está corregido para lo que vamos a hacer en este artículo. Pero aún falta el código del indicador, y si compilas nuevamente el código del servicio con los cambios realizados en el código de encabezado C_Replay.mqh, obtendrás como resultado lo que se ve en la imagen 07.

Figura 07

Imagen 07 - Fallas aún presentadas en el Indicador.

O sea, ahora tenemos que corregir el código del indicador. Al intentar hacerlo, obtendrás como resultado la imagen 08.

Figura 08

Imagen 08 - Fallos originados por la misma causa.

Una vez más, sigue el paso a paso. Empieza siempre por el primer error indicado.

Entonces, si se modifica correctamente el archivo del indicador, se obtendrá el código que se muestra a continuación:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property icon "/Images/Market Replay/Icons/Replay - Device.ico"
04. #property description "Control indicator for the Replay-Simulator service."
05. #property description "This one doesn't work without the service loaded."
06. #property version   "1.49"
07. #property link "https://www.mql5.com/es/articles/11820"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. //+------------------------------------------------------------------+
11. #include <Market Replay\Service Graphics\C_Controls.mqh>
12. //+------------------------------------------------------------------+
13. C_Terminal *terminal = NULL;
14. C_Controls *control = NULL;
15. //+------------------------------------------------------------------+
16. #define def_InfoTerminal (*terminal).GetInfoTerminal()
17. #define def_ShortName       "Market_" + def_SymbolReplay
18. //+------------------------------------------------------------------+
19. int OnInit()
20. {
21.     u_Interprocess Info;
22. 
23.     ResetLastError();
24.     if (CheckPointer(control = new C_Controls(terminal = new C_Terminal())) == POINTER_INVALID) return INIT_FAILED;
25.     if (_LastError != ERR_SUCCESS) return INIT_FAILED;
26.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
27.     IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
28.     if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
29.     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
30.     (*control).Init(Info.s_Infos.isPlay);
31.         
32.     return INIT_SUCCEEDED;
33. }
34. //+------------------------------------------------------------------+
35. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
36. {
37.     static bool bWait = false;
38.     u_Interprocess Info;
39.     
40.     Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
41.     if (!bWait)
42.     {
43.             if (Info.s_Infos.isWait)
44.             {
45.                     EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOn, 1, 0, "");
46.                     bWait = true;
47.             }
48.     }else if (!Info.s_Infos.isWait)
49.     {
50.             EventChartCustom(def_InfoTerminal.ID, C_Controls::ev_WaitOff, 1, Info.df_Value, "");
51.             bWait = false;
52.     }
53.     
54.     return rates_total;
55. }
56. //+------------------------------------------------------------------+
57. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
58. {
59.     (*control).DispatchMessage(id, lparam, dparam, sparam);
60. }
61. //+------------------------------------------------------------------+
62. void OnDeinit(const int reason)
63. {
64.     switch (reason)
65.     {
66.             case REASON_REMOVE:
67.             case REASON_CHARTCLOSE:
68.                     if (def_InfoTerminal.szSymbol != def_SymbolReplay) break;
69.                     GlobalVariableDel(def_GlobalVariableReplay);
70.                     ChartClose(def_InfoTerminal.ID);
71.                     break;
72.     }
73.     delete control;
74.     delete terminal;
75. }
76. //+------------------------------------------------------------------+

Código fuente del Indicador de repetición.

Este código ya está completamente corregido, pero al intentar compilarlo, aún recibirás algunos errores del compilador, los cuales pueden verse en la imagen 09.

Figura 09

Imagen 09 - Intento de compilar el indicador de control.

Entonces necesitamos ir al archivo de encabezado C_Controls.mqh y realizar las correcciones. Pero estas correcciones son bastante simples de hacer. Ya que todo lo que será necesario es eliminar cualquier referencia a u_Value. Así que al final de este proceso, tendrás el archivo visto en el código a continuación:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\Interprocess.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PathBMP                "Images\\Market Replay\\Control\\"
007. #define def_ButtonPlay             def_PathBMP + "Play.bmp"
008. #define def_ButtonPause            def_PathBMP + "Pause.bmp"
009. #define def_ButtonLeft             def_PathBMP + "Left.bmp"
010. #define def_ButtonLeftBlock        def_PathBMP + "Left_Block.bmp"
011. #define def_ButtonRight            def_PathBMP + "Right.bmp"
012. #define def_ButtonRightBlock       def_PathBMP + "Right_Block.bmp"
013. #define def_ButtonPin              def_PathBMP + "Pin.bmp"
014. #define def_ButtonWait             def_PathBMP + "Wait.bmp"
015. #resource "\\" + def_ButtonPlay
016. #resource "\\" + def_ButtonPause
017. #resource "\\" + def_ButtonLeft
018. #resource "\\" + def_ButtonLeftBlock
019. #resource "\\" + def_ButtonRight
020. #resource "\\" + def_ButtonRightBlock
021. #resource "\\" + def_ButtonPin
022. #resource "\\" + def_ButtonWait
023. //+------------------------------------------------------------------+
024. #define def_PrefixObjectName       "Market Replay _ "
025. #define def_NameObjectsSlider      def_PrefixObjectName + "Slider"
026. #define def_PosXObjects            120
027. //+------------------------------------------------------------------+
028. #include "..\Auxiliar\C_Terminal.mqh"
029. #include "..\Auxiliar\C_Mouse.mqh"
030. //+------------------------------------------------------------------+
031. #define def_AcessTerminal (*Terminal)
032. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
033. //+------------------------------------------------------------------+
034. class C_Controls : protected C_Mouse
035. {
036.    protected:
037.            enum EventCustom {ev_WaitOn, ev_WaitOff};
038.    private :
039. //+------------------------------------------------------------------+
040.            string  m_szBtnPlay;
041.            bool            m_bWait;
042.            struct st_00
043.            {
044.                    string  szBtnLeft,
045.                            szBtnRight,
046.                            szBtnPin,
047.                            szBarSlider,
048.                            szBarSliderBlock;
049.                    int     posPinSlider,
050.                            posY,
051.                            Minimal;
052.            }m_Slider;
053.            C_Terminal *Terminal;
054. //+------------------------------------------------------------------+
055. inline void CreateObjectBitMap(int x, int y, string szName, string Resource1, string Resource2 = NULL)
056.                    {
057.                            ObjectCreate(def_InfoTerminal.ID, szName, OBJ_BITMAP_LABEL, 0, 0, 0);
058.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, def_PosXObjects + x);
059.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, y);
060.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 0, "::" + Resource1);
061.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_BMPFILE, 1, "::" + (Resource2 == NULL ? Resource1 : Resource2));
062.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_ZORDER, 1);
063.                    }
064. //+------------------------------------------------------------------+
065. inline void CreteBarSlider(int x, int size)
066.                    {
067.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJ_RECTANGLE_LABEL, 0, 0, 0);
068.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
069.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Slider.posY - 4);
070.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
071.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
072.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
073.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
074.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
075.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
076. //---
077.                            ObjectCreate(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJ_RECTANGLE_LABEL, 0, 0, 0);
078.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
079.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Slider.posY - 9);
080.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
081.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
082.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
083.                    }
084. //+------------------------------------------------------------------+
085.            void CreateBtnPlayPause(bool state)
086.                    {
087.                            m_szBtnPlay = def_PrefixObjectName + "Play";
088.                            CreateObjectBitMap(0, 25, m_szBtnPlay, (m_bWait ? def_ButtonWait : def_ButtonPause), (m_bWait ? def_ButtonWait : def_ButtonPlay));
089.                            ObjectSetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE, state);
090.                    }
091. //+------------------------------------------------------------------+
092.            void CreteCtrlSlider(void)
093.                    {
094.                            u_Interprocess Info;
095.                            
096.                            m_Slider.szBarSlider      = def_NameObjectsSlider + " Bar";
097.                            m_Slider.szBarSliderBlock = def_NameObjectsSlider + " Bar Block";
098.                            m_Slider.szBtnLeft        = def_NameObjectsSlider + " BtnL";
099.                            m_Slider.szBtnRight       = def_NameObjectsSlider + " BtnR";
100.                            m_Slider.szBtnPin         = def_NameObjectsSlider + " BtnP";
101.                            m_Slider.posY = 40;
102.                            CreteBarSlider(77, 436);
103.                            CreateObjectBitMap(47, 25, m_Slider.szBtnLeft, def_ButtonLeft, def_ButtonLeftBlock);
104.                            CreateObjectBitMap(511, 25, m_Slider.szBtnRight, def_ButtonRight, def_ButtonRightBlock);
105.                            CreateObjectBitMap(0, m_Slider.posY, m_Slider.szBtnPin, def_ButtonPin);
106.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_ANCHOR, ANCHOR_CENTER);
107.                            if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.df_Value = 0;
108.                            m_Slider.Minimal = Info.s_Infos.iPosShift;
109.                            PositionPinSlider(Info.s_Infos.iPosShift);
110.                    }
111. //+------------------------------------------------------------------+
112. inline void RemoveCtrlSlider(void)
113.                    {                       
114.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
115.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_NameObjectsSlider);
116.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, true);
117.                    }
118. //+------------------------------------------------------------------+
119. inline void PositionPinSlider(int p, const int minimal = 0)
120.                    {
121.                            m_Slider.posPinSlider = (p < minimal ? minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
122.                            m_Slider.posPinSlider = (p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
123.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnPin, OBJPROP_XDISTANCE, m_Slider.posPinSlider + def_PosXObjects + 95);
124.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != minimal);
125.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnLeft, OBJPROP_STATE, m_Slider.posPinSlider != m_Slider.Minimal);
126.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBtnRight, OBJPROP_STATE, m_Slider.posPinSlider < def_MaxPosSlider);
127.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, minimal + 2);
128.                            ObjectSetInteger(def_InfoTerminal.ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
129.                            ChartRedraw();
130.                    }
131. //+------------------------------------------------------------------+
132.    public  :
133. //+------------------------------------------------------------------+
134.            C_Controls(C_Terminal *arg)
135.                    :C_Mouse(arg),
136.                     m_bWait(false)
137.                    {
138.                            if (CheckPointer(Terminal = arg) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
139.                            m_szBtnPlay          = NULL;
140.                            m_Slider.szBarSlider = NULL;
141.                            m_Slider.szBtnPin    = NULL;
142.                            m_Slider.szBtnLeft   = NULL;
143.                            m_Slider.szBtnRight  = NULL;
144.                    }
145. //+------------------------------------------------------------------+
146.            ~C_Controls()
147.                    {
148.                            if (CheckPointer(Terminal) == POINTER_INVALID) return;
149.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, false);
150.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_PrefixObjectName);
151.                    }
152. //+------------------------------------------------------------------+
153.            void Init(const bool state)
154.                    {
155.                            CreateBtnPlayPause(state);
156.                            GlobalVariableTemp(def_GlobalVariableReplay);
157.                            if (!state) CreteCtrlSlider();
158.                            ChartRedraw();
159.                    }
160. //+------------------------------------------------------------------+
161.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
162.                    {
163.                            u_Interprocess Info;
164.                            static int six = -1, sps;
165.                            int x, y, px1, px2;
166.                            
167.                            C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
168.                            switch (id)
169.                            {
170.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOn):
171.                                            if (lparam == 0) break;
172.                                            m_bWait = true;
173.                                            CreateBtnPlayPause(true);
174.                                            break;
175.                                    case (CHARTEVENT_CUSTOM + C_Controls::ev_WaitOff):
176.                                            if (lparam == 0) break;
177.                                            m_bWait = false;
178.                                            Info.df_Value = dparam;
179.                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
180.                                            break;
181.                                    case CHARTEVENT_OBJECT_DELETE:
182.                                            if (StringSubstr(sparam, 0, StringLen(def_PrefixObjectName)) == def_PrefixObjectName)
183.                                            {
184.                                                    if (StringSubstr(sparam, 0, StringLen(def_NameObjectsSlider)) == def_NameObjectsSlider)
185.                                                    {
186.                                                            RemoveCtrlSlider();
187.                                                            CreteCtrlSlider();
188.                                                    }else
189.                                                    {
190.                                                            Info.df_Value = GlobalVariableGet(def_GlobalVariableReplay);
191.                                                            CreateBtnPlayPause(Info.s_Infos.isPlay);
192.                                                    }
193.                                                    ChartRedraw();
194.                                            }
195.                                            break;
196.                                    case CHARTEVENT_OBJECT_CLICK:
197.                                            if (m_bWait) break;
198.                                            if (sparam == m_szBtnPlay)
199.                                            {
200.                                                    Info.s_Infos.isPlay = (bool) ObjectGetInteger(def_InfoTerminal.ID, m_szBtnPlay, OBJPROP_STATE);
201.                                                    if (!Info.s_Infos.isPlay) CreteCtrlSlider(); else
202.                                                    {
203.                                                            RemoveCtrlSlider();
204.                                                            m_Slider.szBtnPin = NULL;
205.                                                    }
206.                                                    Info.s_Infos.iPosShift = (ushort) m_Slider.posPinSlider;
207.                                                    GlobalVariableSet(def_GlobalVariableReplay, Info.df_Value);
208.                                                    ChartRedraw();
209.                                            }else   if (sparam == m_Slider.szBtnLeft) PositionPinSlider(m_Slider.posPinSlider - 1);
210.                                            else if (sparam == m_Slider.szBtnRight) PositionPinSlider(m_Slider.posPinSlider + 1);
211.                                            break;
212.                                    case CHARTEVENT_MOUSE_MOVE:
213.                                            if (GetInfoMouse().ExecStudy) return;
214.                                            if ((CheckClick(C_Mouse::eClickLeft)) && (m_Slider.szBtnPin != NULL))
215.                                            {
216.                                                    x = GetInfoMouse().Position.X;
217.                                                    y = GetInfoMouse().Position.Y;
218.                                                    px1 = m_Slider.posPinSlider + def_PosXObjects + 86;
219.                                                    px2 = m_Slider.posPinSlider + def_PosXObjects + 114;
220.                                                    if ((y >= (m_Slider.posY - 14)) && (y <= (m_Slider.posY + 14)) && (x >= px1) && (x <= px2) && (six == -1))
221.                                                    {
222.                                                            six = x;
223.                                                            sps = m_Slider.posPinSlider;
224.                                                            ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false);
225.                                                    }
226.                                                    if (six > 0) PositionPinSlider(sps + x - six);
227.                                            }else if (six > 0)
228.                                            {
229.                                                    six = -1;
230.                                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true);
231.                                            }
232.                                            break;
233.                            }
234.                    }
235. //+------------------------------------------------------------------+
236. };
237. //+------------------------------------------------------------------+
238. #undef def_InfoTerminal
239. #undef def_AcessTerminal
240. #undef def_PosXObjects
241. #undef def_ButtonPlay
242. #undef def_ButtonPause
243. #undef def_ButtonLeft
244. #undef def_ButtonRight
245. #undef def_ButtonPin
246. #undef def_NameObjectsSlider
247. #undef def_PrefixObjectName
248. #undef def_PathBMP
249. //+------------------------------------------------------------------+

Código fuente del archivo C_Controls.mqh

Hechas estas correcciones, cambios y ajustes, podemos intentar compilar nuevamente el servicio de repetición/simulador. Y como resultado, obtendrás la siguiente imagen:

Figura 10

Imagen 10: Compilación final.

Esto indica que el servicio se compiló correctamente, y observa que el indicador también se compiló. Esto indica que ya puedes eliminar simplemente el ejecutable del indicador, usando el explorador de archivos, ya que ahora formará parte del ejecutable del servicio de repetición/simulación. Sin embargo, si eliminas el ejecutable del indicador y tratas de ejecutar el sistema de repetición/simulación en MetaTrader 5, verás que se abre el gráfico, pero el indicador de control no aparece. ¿Por qué?

El motivo es que el indicador de control realmente lo está iniciando la plantilla y no el servicio. Y en la plantilla, no hay nada que indique que el indicador de control forma parte del ejecutable del servicio. Puedes notar esto observando el contenido del archivo de plantilla. Pero, como la idea no es utilizar la plantilla, sino el servicio, para iniciar el indicador de control en el gráfico, no entraré en detalles sobre cómo resolver esta cuestión. Pero centrémonos en lo que realmente queremos hacer.

Como lo que necesitamos hacer no es algo tan simple y no quiero extenderme demasiado en este artículo, dejaré las modificaciones para el próximo. Esto se debe a que será necesario modificar muchas cosas para que el Indicador de control continúe siendo funcional y, al mismo tiempo, podamos dejar el servicio de repetición/simulador libre, ligero y listo para que tú, como usuario, puedas usar indicadores, estrategias o modelos personales. La idea de hacer estos cambios es precisamente para promover este tipo de cosas. El objetivo es permitir que uses el sistema con tus propios conceptos e ideas.

Pero el motivo principal por el que dejamos para mostrar en el próximo artículo lo que necesitamos hacer para que el indicador de control aparezca en el gráfico, cuando es un recurso del servicio y sin que necesitemos usar una plantilla para ello, es el hecho de que será necesario eliminar algo que está duplicado en el modo de funcionamiento del sistema. Esto hace que el indicador de control sea extremadamente inestable si se utiliza directamente a través del servicio.


Conclusión

A pesar de que hemos modificado aquí, en este artículo, el indicador de control y el servicio hasta el punto de poder empezar a eliminar el ejecutable del indicador de la lista de indicadores accesibles al usuario, el sistema en sí se ha mostrado inestable debido a una pendiente mal resuelta entre las formas de interacción del indicador de control y el usuario. Dicha inestabilidad se debe a que es necesario que haya otras cosas en el gráfico. Se trata de cosas que forman parte del archivo de plantilla, pero que no quiero utilizar cuando el servicio de repetición/simulación esté en uso. Quiero y voy a mostrar cómo debemos proceder para hacer que el servicio sea autosostenible, de modo que el usuario pueda utilizar sus propias plantillas o configuraciones.

Al mismo tiempo, estaremos creando una forma adecuada de promover el servicio de repetición/simulación, para que puedas practicar tu estrategia o modelo de análisis.

Todavía nos queda mucho por hacer antes de que este sistema pueda recibir el tan esperado conjunto para trabajar con órdenes, posiciones de repetición y simulación. Espero que, querido lector, comprendas el nivel y grado de complejidad que estamos alcanzando. Y todo esto usando únicamente MQL5, sin programación externa o cosas por el estilo. De hecho, este sistema ha demostrado ser bastante desafiante. Pero me gustan los desafíos y este está siendo bastante motivador.

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

Archivos adjuntos |
Anexo.zip (420.65 KB)
Desarrollo de un sistema de repetición (Parte 50): Esto complica las cosas (II) Desarrollo de un sistema de repetición (Parte 50): Esto complica las cosas (II)
Vamos resolver la cuestión del ID del gráfico, pero al mismo tiempo, vamos empezar a garantizar que el usuario pueda hacer uso de una plantilla personal, enfocada en analizar el activo que desea estudiar y simular. El contenido expuesto aquí tiene como objetivo, pura y simplemente, ser didáctico. En ningún caso debe considerarse como una aplicación cuya finalidad no sea el aprendizaje y el estudio de los conceptos mostrados.
Desarrollo de un sistema de repetición (Parte 48): Conceptos que hay que entender y comprender Desarrollo de un sistema de repetición (Parte 48): Conceptos que hay que entender y comprender
¿Qué tal aprender algo nuevo? En este artículo, aprenderás cómo transformar scripts y servicios y por qué es útil hacerlo.
El tipo de dibujado DRAW_ARROW en indicadores de símbolo y periodo múltiple El tipo de dibujado DRAW_ARROW en indicadores de símbolo y periodo múltiple
En este artículo nos ocuparemos de dibujar los indicadores de símbolo y periodo múltiple. Asimismo, mejoraremos los métodos de la clase para representar correctamente las flechas que muestran los datos de los indicadores de flecha calculados sobre un símbolo/periodo que no se corresponde con el símbolo/periodo del gráfico actual.
Desarrollo y prueba de los sistemas comerciales Aroon Desarrollo y prueba de los sistemas comerciales Aroon
En este artículo, aprenderemos a construir un sistema comercial Aroon, aprendiendo asimilando los fundamentos de los indicadores y los pasos necesarios para crear un sistema comercial basado en el indicador Aroon. Una vez creado este sistema comercial, comprobaremos si puede ser rentable o necesita una mayor optimización.