![Desarrollo de un sistema de repetición (Parte 57): Diseccionamos el servicio de prueba](https://c.mql5.com/2/85/Desenvolvendo_um_sistema_de_Replay_Parte_57_600x314.jpg)
Desarrollo de un sistema de repetición (Parte 57): Diseccionamos el servicio de prueba
Introducción
En el artículo anterior, Desarrollo de un sistema de repetición (Parte 56): Adecuación de los módulos, hicimos algunos cambios, tanto en el módulo del indicador de control como, principalmente, en el módulo del indicador de mouse.
Debido a que el artículo ya contenía bastante información, se decidió que no se agregarían más datos, ya que esto probablemente te confundiría más, querido lector, en lugar de ayudar a esclarecer y explicar cómo las cosas realmente funcionaban.
En el anexo de ese mismo artículo, tienes acceso al indicador de mouse y a un servicio. Cuando lo pones en funcionamiento, se crea un activo personalizado y se añade una barra con el indicador de mouse y el indicador de control. Ambos módulos se colocan en el gráfico del activo personalizado y, aunque no ejecutan ninguna actividad relacionada con el servicio, se puede notar una cierta actividad de interacción entre el usuario y ambos módulos.
A lo mejor, tú, especialmente si tienes menos familiaridad con MQL5, no puedas imaginar cómo se hace. Al observar el código de ambos indicadores, no logras ver ni percibir ninguna actividad que evite que los datos presentes en el indicador, o, mejor dicho, en el módulo de control, se pierdan.
Muy bien, para explicar esto en detalle y con la debida calma, este artículo se centrará en explicar cómo funciona realmente el servicio que contiene dicho artículo. Esta explicación es de suma importancia, ya que para comprender cómo funciona el servicio, y principalmente este, será fundamental entender cómo trabaja el sistema de repetición/simulación. Esto se debe a que es más sencillo crear y explicar un código con muchas menos cosas involucradas que intentar entender un código en el que el trabajo es mucho más complejo.
Así que, aunque este código no sea algo que vayamos a utilizar posteriormente, es importante que lo comprendas en detalle. Ya que toda la base de la interacción entre el módulo de control, el módulo del mouse y el servicio se asimilará mucho mejor si entiendes este código más simple.
Entonces, sin más preámbulos, veamos el código fuente del servicio que se implementó y que se vio en el artículo anterior, para entender lo que se puede ver en el video que está al final de ese mismo artículo.
Análisis del código del servicio
A continuación, se muestra el código fuente del servicio completo:
01. //+------------------------------------------------------------------+ 02. #property service 03. #property copyright "Daniel Jose" 04. #property description "Data synchronization demo service." 05. #property link "https://www.mql5.com/pt/articles/12000" 06. #property version "1.00" 07. //+------------------------------------------------------------------+ 08. #include <Market Replay\Defines.mqh> 09. //+------------------------------------------------------------------+ 10. #define def_IndicatorControl "Indicators\\Market Replay.ex5" 11. #resource "\\" + def_IndicatorControl 12. //+------------------------------------------------------------------+ 13. #define def_Loop ((!_StopFlag) && (ChartSymbol(id) != "")) 14. //+------------------------------------------------------------------+ 15. void OnStart() 16. { 17. uCast_Double info; 18. long id; 19. int handle; 20. short iPos, iMode; 21. double Buff[]; 22. MqlRates Rate[1]; 23. 24. SymbolSelect(def_SymbolReplay, false); 25. CustomSymbolDelete(def_SymbolReplay); 26. CustomSymbolCreate(def_SymbolReplay, StringFormat("Custom\\%s", def_SymbolReplay)); 27. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_SIZE, 0.5); 28. CustomSymbolSetDouble(def_SymbolReplay, SYMBOL_TRADE_TICK_VALUE, 5); 29. Rate[0].close = 110; 30. Rate[0].open = 100; 31. Rate[0].high = 120; 32. Rate[0].low = 90; 33. Rate[0].tick_volume = 5; 34. Rate[0].time = D'06.01.2023 09:00'; 35. CustomRatesUpdate(def_SymbolReplay, Rate, 1); 36. SymbolSelect(def_SymbolReplay, true); 37. id = ChartOpen(def_SymbolReplay, PERIOD_M30); 38. if ((handle = iCustom(ChartSymbol(id), ChartPeriod(id), "\\Indicators\\Mouse Study.ex5", id)) != INVALID_HANDLE) 39. ChartIndicatorAdd(id, 0, handle); 40. IndicatorRelease(handle); 41. if ((handle = iCustom(ChartSymbol(id), ChartPeriod(id), "::" + def_IndicatorControl, id)) != INVALID_HANDLE) 42. ChartIndicatorAdd(id, 0, handle); 43. IndicatorRelease(handle); 44. Print("Service maintaining sync state. Version Demo..."); 45. iPos = 0; 46. iMode = SHORT_MIN; 47. while (def_Loop) 48. { 49. while (def_Loop && ((handle = ChartIndicatorGet(id, 0, "Market Replay Control")) == INVALID_HANDLE)) Sleep(50); 50. info.dValue = 0; 51. if (CopyBuffer(handle, 0, 0, 1, Buff) == 1) info.dValue = Buff[0]; 52. IndicatorRelease(handle); 53. if ((short)(info._16b[0]) == SHORT_MIN) 54. { 55. info._16b[0] = (ushort)iPos; 56. info._16b[1] = (ushort)iMode; 57. EventChartCustom(id, evCtrlReplayInit, 0, info.dValue, ""); 58. }else if (info._16b[1] != 0) 59. { 60. iPos = (short)info._16b[0]; 61. iMode = (short)info._16b[1]; 62. } 63. Sleep(250); 64. } 65. ChartClose(id); 66. SymbolSelect(def_SymbolReplay, false); 67. CustomSymbolDelete(def_SymbolReplay); 68. Print("Finished service..."); 69. } 70. //+------------------------------------------------------------------+
Código fuente del servicio de prueba
Para que todos puedan entender realmente lo que está ocurriendo, incluso aquellos con menos experiencia en MQL5. Y especialmente aquellos que no están tan familiarizados con la programación de servicios para MetaTrader 5. Vamos a desglosar detalladamente estos códigos, comenzando en la línea 2.
Cuando encuentres en un código MQL5 la propiedad que se declara en la línea 2, debes entender que se trata de un servicio. Si esta propiedad no estuviera presente, el código debería considerarse un script. Las principales diferencias entre un servicio y un script se encuentran, en primer lugar, en la existencia o no de esta propiedad en términos de código. En términos de ejecución, la principal diferencia es que un script siempre estará vinculado a un gráfico, mientras que un servicio no depende de la existencia de un gráfico abierto.
El resto de las líneas, entre la 3 y la 6, son bastante conocidas para quienes tienen conocimientos mínimos de programación MQL5. En este caso, podemos avanzar sin darles mucha importancia.
En la línea 8, hemos añadido una directiva include para traer un archivo de cabecera dentro de este código. Ten en cuenta que, en este caso, el archivo de cabecera estará en la carpeta "Include" dentro de la carpeta "Market Replay", y se llama "Defines.mqh". La carpeta Include se encuentra en la raíz del directorio principal MQL5, que será referenciado por el MetaEditor en el momento en que se ejecute la compilación.
Ahora es necesario prestar especial atención a las líneas 10 y 11. Estas líneas están ahí para que el indicador, o, mejor dicho, el módulo de control se convierta en un recurso interno del código compilado del servicio. ¿Qué significa esto realmente? Significa que, cuando transportes el código ya compilado, no necesitarás llevar el código del módulo de control, ya que estará incorporado como un recurso del servicio. Por esta razón, en el anexo del artículo anterior, solo había dos ejecutables, aunque en el momento en que el servicio entraba en funcionamiento, se estaban ejecutando realmente tres archivos.
Pero, ¿por qué no incluimos también el indicador, o, mejor dicho, el módulo de mouse como un recurso del servicio? ¿Por qué no hicimos lo mismo con el módulo de control? La razón es sencilla: permitir que el usuario pueda usar y tener un acceso simplificado al módulo de mouse. Si este módulo se integrara en el ejecutable del servicio, el usuario tendría dificultades para acceder al módulo de mouse y poder colocarlo en un gráfico diferente del que crearía el servicio.
Por esta razón, a menudo necesitamos tomar decisiones sobre qué cosas y, principalmente, por qué hacer que ciertos elementos sean recursos internos de algún ejecutable.
A continuación, vemos la línea 13. En esta línea, declaramos una definición que tiene como objetivo simplificar, o, mejor dicho, estandarizar algún tipo de prueba que vamos a realizar. Es bastante común en programas complejos tener que ejecutar el mismo tipo de prueba en varios puntos. El hecho de crear una definición para estandarizar dichas pruebas, además de simplificar y facilitar el mantenimiento del código, garantiza que siempre ejecutaremos el mismo tipo de prueba. En muchas situaciones, esto es extremadamente deseable para la mayoría de los programadores, ya que se podrían olvidar de modificar un punto de prueba específico y, cuando el código ejecute esa prueba específica, no coincidirá con otros puntos. Esto suele causar muchos dolores de cabeza y molestias.
Muy bien. En la línea 15 es cuando realmente empezamos con la parte ejecutable del código. Observa que OnStart es el mismo punto de entrada tanto en scripts como en servicios. Esto es para el código en MQL5. Sin embargo, debido a la declaración de la línea 2, sabemos que estamos tratando con un servicio. MetaTrader 5 generará solo una llamada a este evento OnStart cada vez que se ejecute el código. Por tanto, serán necesarias algunas maniobras para mantener el código en funcionamiento durante todo el tiempo que sea necesario. Sin embargo, en este caso específico, esto solo ocurrirá en la línea 47. Pero antes, necesitamos hacer algunas cosas.
Las cosas a las que acabo de referirme son la inicialización y adecuación del servicio, para que pueda hacer algo útil para nosotros. Entre estos pasos se encuentran la declaración e inicialización de variables y condiciones que se impondrán a MetaTrader 5 desde nuestro servicio. Todo ello se llevará a cabo una vez que el servicio entre en funcionamiento. Por ello, entre las líneas 17 y 22, declaramos las variables que usaremos y procedemos a la correcta inicialización de las reglas que se impondrán a MetaTrader 5. Mucho de lo que se verá a partir de este momento podrá parecer extraño.
Pero se trata de cosas aparentemente simples que, en primera instancia, podrían formar parte de otro tipo de aplicación. Sin embargo, como queremos concentrar las cosas en un solo código, trabajaremos de esta manera.
Luego, en la línea 24, indicamos a MetaTrader 5 que el activo al que nos referimos debe eliminarse de la ventana de observación de mercado. Justo después, en la línea 25, lo eliminamos de la lista de activos. Esta lista contiene todos los activos a los que podemos tener algún tipo de acceso. Pero lo que realmente nos interesa viene en la línea 26. En esta línea, indicamos a MetaTrader 5 que queremos crear un activo personalizado y especificamos dónde debe crearse. Debes prestar atención a esto, ya que, si no tienes el debido cuidado, podrías sobrescribir un activo de mercado al hacer tal cosa y, al intentar acceder al activo real, en realidad estarías accediendo al activo personalizado. Pero normalmente tomamos siempre algunas precauciones para evitar que esto ocurra.
Las líneas 26 y 27 son imprescindibles; sin ellas, el módulo de mouse no podría ajustar correctamente la línea de precio en la posición correcta al mover el ratón por el gráfico.
Ahora, entre las líneas 29 y 34, definimos la primera barra que se verá realmente en el gráfico. Por motivos que no logro comprender del todo, dicha barra siempre se coloca en una posición en la que no es posible visualizar los máximos y mínimos. Pero, ya que el objetivo aquí es simplemente presentar una barra en el gráfico y evitar que los módulos generen algún tipo de error de rango, el hecho de que la barra sea completamente visible o no nos importa mucho.
Hasta este momento, de hecho, no tenemos absolutamente nada en el gráfico, ni siquiera un gráfico. En la línea 35, le decimos al MetaTrader 5 que los valores definidos en las líneas anteriores, donde describimos la barra, deben colocarse como la primera barra del activo que estamos creando. En la línea 36, le indicamos que el activo debe colocarse en la ventana de observación de mercado. Si el código terminara aquí, podríamos abrir manualmente el activo personalizado y visualizar la barra que colocamos en él. Pero, como queremos automatizar aún más cosas, tenemos la línea 37.
En el momento exacto en que se ejecute la línea 37, el MetaTrader 5 abrirá un gráfico con el activo indicado y en el período gráfico que hayamos indicado. Aunque ya se ha explicado en otras ocasiones, incluso en un momento dado de esta secuencia, expliqué el motivo de informar a los módulos del ID del gráfico. Es importante recordar que ahora tenemos un gráfico abierto y que, al no especificar qué plantilla debe usarse, el gráfico se abrirá con la plantilla predeterminada. Este conocimiento es fundamental para comprender las siguientes líneas.
Vamos a empezar haciendo lo siguiente: Fíjate en que en la línea 38 intentamos generar un "handle", o manipulador, para intentar colocar el módulo de mouse en el gráfico. Presta mucha atención a la ubicación que se indica y al nombre del ejecutable. Si el intento de creación del handle no tiene éxito por cualquier motivo, la línea 39 no se ejecutará. Normalmente, la razón es que el ejecutable no se encuentra en el lugar indicado. Más adelante explicaré un poco más sobre este tema. Pero si se crea efectivamente el handle, en la línea 39 agregaremos el módulo al gráfico. Así, el indicador será accesible, no necesariamente visible, pero podrá estar disponible en la lista de indicadores del gráfico.
Entonces, en la línea 40, le decimos al MetaTrader 5 que el handle ya no es necesario, por lo que la memoria asignada puede devolverse al sistema. Sin embargo, dado que el módulo ya se encuentra en el gráfico, el MetaTrader 5 no lo eliminará del gráfico, a menos que le indiques al MetaTrader 5 que deseas eliminar el módulo del gráfico.
Este mismo script se podría eliminar y el efecto sería el mismo, es decir, el módulo de mouse se colocaría en el gráfico. Para ello, bastaría con que la plantilla predeterminada lo contuviera. Para ello, abrirías cualquier gráfico, agregarías el indicador de mouse a ese gráfico y, luego, guardarías esta configuración con el nombre de Default.tpl. O, para mayor claridad, estarías creando la plantilla predeterminada ahora con el indicador de mouse dentro de esta plantilla. Hacer esto eliminaría la necesidad de los comandos vistos entre las líneas 38 y 40 de hecho existieran. Al mismo tiempo, permitiría colocar el indicador de mouse en cualquier lugar más adecuado a lo que se quiere hacer.
En el caso del módulo de control, la situación es un poco diferente. Esto se debe a que el módulo de control está integrado en el ejecutable del servicio. El simple hecho de que esto ocurra facilita que el servicio coloque realmente dicho módulo en el gráfico. Esto se realiza en las líneas 41 y 42. En la línea 41, generamos un handle para poder acceder al módulo, y en la línea 42, lo añadimos al gráfico. Observa que, si no usamos la línea 42, el módulo no se colocaría en el gráfico, solo se cargaría en la memoria, pero el MetaTrader 5 no lo lanzaría al gráfico que deseamos.
En la línea 43, hemos eliminado el handle, ya que no lo necesitamos más, como ocurrió en la línea 40.
Hasta este momento, el servicio se comporta como un programa que se cerrará inmediatamente. Pero antes, usamos la línea 44 para imprimir una mensaje en la caja de mensajes con el fin de saber hasta dónde hemos llegado en la ejecución. Luego, entre las líneas 45 y 46, inicializamos las últimas variables que de hecho vamos a usar. Presta atención a este hecho: estamos inicializando estas variables con valores que se usarán para que el módulo de control pueda iniciarse. Recuerda que solo se ha lanzado en el gráfico, pero aún no se ha inicializado y, por esta razón, no se muestra en él.
Finalmente, en la línea 47, entramos en un bucle. Este se terminará si alguna de las condiciones definidas en la línea 13 se cumple, lo que hará que la condición sea falsa y que el bucle se cierre. A partir de este momento, no haremos las cosas de cualquier manera. A partir de este momento, el servicio dejará de ejecutar acciones y se encargará de gestionar las ya en ejecución. Es importante tener presente este concepto y saber hacer tal diferenciación. De lo contrario, intentarás hacer cosas que no deberían estar en el bucle, lo que volvería todo muy inestable y problemático.
Ahora viene un nuevo bucle en la línea 49. Este bucle de la línea 49 es peligroso si no se planifica adecuadamente. Esto se debe a que, si no utilizáramos la definición de la línea 13, podríamos quedar atrapados en este bucle indefinidamente. Esto se debe a que el módulo de control podría no estar presente en el gráfico, y el servicio esperaría a que el MetaTrader 5 le informara del valor del manipulador para poder acceder al indicador o módulo de control.
Sin embargo, al eliminar el módulo de control del gráfico, el MetaTrader 5 cerrará el gráfico. Esto hace que la definición de la línea 13 sea falsa, por lo que tanto el bucle de la línea 49 como el de la línea 47 se cerrarán.
Precisamente por esta razón, estoy explicando el funcionamiento de las cosas en este sistema más simple. Percibir y notar esto en un sistema más elaborado sería mucho más difícil. Por eso, siempre que quieras probar algo, hazlo con un programa más simple que siga la misma lógica de funcionamiento del sistema que se proyectará después.
Entonces, suponiendo que el MetaTrader 5 nos devuelva un handle válido en la línea 50, ponemos a cero el valor que definiremos si en la línea 51 la lectura del buffer del indicador de control es exitosa.
Observa lo siguiente: Si la lectura del buffer falla por cualquier motivo, la variable que usaremos estará en cero. Pero si la lectura es exitosa, tendremos los datos que se encuentran en el buffer del indicador de control.
Una vez que obtenemos los datos, podemos eliminar el manipulador. Esto se hace en la línea 52. La razón es que durante las próximas etapas puede pasar cualquier cosa y no queremos tener señales falsas cuando se repita el bucle. Esto podría parecer algo que disminuyera el rendimiento general del sistema, pero es mejor perder un poco de rendimiento que analizar datos que podrían ser inválidos o basura.
Ahora, presta atención a algo: hasta el momento, el indicador, o, mejor dicho, el módulo de control no está inicializado. Sin embargo, su buffer contiene valores que se colocaron allí durante la primera fase de funcionamiento del indicador. Para entender esto, consulta los artículos anteriores de esta secuencia. Por tanto, no debes esperar que el retorno de la lectura del buffer sea valores cero, porque eso no ocurrirá.
Por esta razón, tenemos dos pruebas condicionales. Una para inicializar el módulo de control, de manera que sepa cuáles son los controles que debe mostrar. Y otra prueba, para almacenar aquí en el servicio lo que está ocurriendo en el módulo de control. Esto es para que, cuando sea necesario informar nuevamente la última condición de funcionamiento del módulo, sepamos qué valores informar al módulo.
Entonces, en la línea 53, verificamos si el módulo se ha añadido recientemente al gráfico. Esto puede ocurrir en dos momentos. El primero se produce cuando aún estamos en la primera ejecución del bucle iniciado en la línea 47. En este caso, los valores que se han de informar al módulo de control se obtienen de las líneas 45 y 46. El segundo momento se produce cuando el MetaTrader 5 tiene que reponer el módulo de control en el gráfico porque el tiempo del gráfico se ha alterado por cualquier motivo. En este caso, los valores que se deben utilizar serán los últimos que estaban en el indicador de control antes de que el MetaTrader 5 lo volviera a colocar en el gráfico.
En cualquier caso, al final ejecutaremos en la línea 57, lo que hará que el MetaTrader 5 genere un evento personalizado en el gráfico que el servicio está observando. De esta forma, el indicador de control recibirá los nuevos valores o los valores antiguos, dando la impresión de que, de alguna manera mágica, nada se ha perdido. Esto solo ocurrirá porque en el indicador, en el módulo de control, hemos colocado un valor determinado en el buffer, lo que indica que el indicador desea y necesita recibir valores actualizados de una fuente externa. En este caso, el servicio que se está ejecutando en la plataforma MetaTrader 5.
Ahora, si este no es el contenido procedente del buffer del indicador, tenemos una nueva prueba que se está ejecutando en la línea 58, donde se verifica si el valor es diferente de cero. Si ocurre esto, significa que el indicador de control está en "play" o en pausa. Sin embargo, aquí, en el servicio de demostración, ambas situaciones indican que el servicio absorberá lo que está en el buffer del indicador, almacenando así el valor para ser utilizado si el MetaTrader 5 decide eliminar y volver a colocar el indicador en el gráfico, debido a una alteración en el tiempo gráfico.
Dado que la ejecución puede suceder muy rápidamente y no necesitamos una actualización ultrarrápida, utilizamos la línea 63 para proporcionar un cierto descanso al servicio, tiempo durante el cual este no ejecutará nada importante. Esta línea también nos indica el final del bucle que comenzó en la línea 47.
Además, cuando el usuario cierre el servicio o el gráfico se cierre por cualquier motivo, saltaremos del bucle iniciado en la línea 47 y ejecutaremos primero la línea 65. En esta línea, cerraremos el gráfico, ya que, si el usuario cierra el servicio, el gráfico aún permanecerá abierto. Para evitar esto, indicamos a MetaTrader 5 que puede cerrar el gráfico que estamos utilizando.
Después de cerrar el gráfico, intentamos eliminar del panel de observación de mercado el símbolo personalizado que se ha creado. Esto se intenta en la línea 66. Si no hay ningún otro gráfico abierto con este símbolo, esperamos a que MetaTrader 5 lo elimine de la observación de mercado. Sin embargo, incluso después de haber sido eliminado, seguirá estando listado en el punto donde fue creado, en la línea 26.
Si de hecho se ha eliminado el símbolo de la observación de mercado, usaremos la línea 67 para eliminarlo ahora de la lista de símbolos publicados en el sistema. Finalmente, en la línea 68, si todo ha ido bien y todos los procedimientos se han ejecutado según lo esperado, imprimiremos un mensaje en la caja de mensajes del terminal en el que informaremos de que el servicio ha finalizado y ya no tenemos el soporte para su ejecución.
Conclusión
Este tipo de situación, aunque aparentemente pueda parecer sin importancia o inútil a largo plazo, en realidad proporciona conocimiento útil y necesario para lo que de hecho necesitaremos hacer después. Muchas veces, cuando desarrollamos alguna solución o aplicación, tenemos que lidiar con escenarios desconocidos o poco favorables. Por lo tanto, siempre que necesites diseñar, programar o desarrollar algo cuyo trabajo sea mucho más complejo de lo que inicialmente puedas planear, crea un pequeño programa que sea sencillo de comprender y estudiar, pero que, sin embargo, ejecute parte del trabajo que necesitas realizar a largo plazo y cuya tarea sea extremadamente compleja de implementar por completo.
Si haces las cosas de la manera correcta, podrás tomar parte del pequeño programa que creaste y usarlo en el proyecto en el que realmente estabas trabajando. Por esta razón, decidí crear este artículo. Para que entiendas que un programa, que a veces es extremadamente complejo y que implica diversos niveles de conocimiento, no se crea de la nada.
Siempre se crea por etapas que funcionan y, luego, parte de lo concebido se utiliza para resolver el problema mayor. Y precisamente esto es lo que haremos aquí, en el sistema de repetición/simulador. La manera en que este servicio, descrito en este artículo, funciona, nos proporciona exactamente lo que necesitamos implementar en nuestro sistema en construcción.
Observa que, sin los análisis y estudios adecuados que este artículo permitió hacer, sería poco probable que tuviéramos éxito, con un mínimo de esfuerzo, para que los módulos funcionaran adecuadamente en el sistema de repetición/simulador. Tendríamos que realizar varios ajustes y cambios en el código, pero todos ellos se concentrarían en un único punto: la clase C_Replay. Pero, sin saber cómo y de qué forma adecuar la clase C_Replay, acabaríamos en un callejón sin salida y la implementación de lo visto aquí sería algo extremadamente complejo.
Pero ahora que sabemos cómo se comportará en realidad el sistema, todo lo que necesitamos hacer es crear algunas pocas modificaciones, (así espero) en la clase C_Replay, para que el bucle presente entre las líneas 47 y 64 pueda formar parte del código de nuestro sistema. Con un pequeño detalle: a diferencia de lo que vemos aquí, en el sistema de repetición/simulador haremos un cálculo para seguir lo que ya ha sido graficado. Pero esto se verá en los próximos artículos. Así que estudia con calma lo visto en este artículo. Entiende y aprende cómo funciona este sistema para poder comprender lo que se verá en los próximos artículos. Ahora haremos que el servicio de repetición/simulador use estos mismos módulos que están funcionando, como puedes observar en el video del artículo anterior.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/12005
![Desarrollo de un sistema de repetición (Parte 58): Volvemos a trabajar en el servicio](https://c.mql5.com/2/85/Desenvolvendo_um_sistema_de_Replay_Parte_58__LOGO.png)
![Desarrollo de un sistema de repetición (Parte 56): Adecuación de los módulos](https://c.mql5.com/2/83/Desenvolvendo_um_sistema_de_Replay_Parte_56__LOGO_3_.png)
![Redes neuronales: así de sencillo (Parte 74): Predicción de trayectorias con adaptación](https://c.mql5.com/2/65/Neural_networks_are_easy_4Part_74w_Adaptive_trajectory_prediction____LOGO.png)
![Desarrollamos un Asesor Experto multidivisas (Parte 3): Revisión de la arquitectura](https://c.mql5.com/2/70/Developing_a_multi-currency_advisor_0Part_1g___LOGO__3.png)
![MQL5 - Lenguaje de estrategias comerciales para el terminal de cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso