English Русский 中文 Deutsch 日本語 Português
preview
Desarrollo de un EA comercial desde cero (Parte 31): Rumbo al futuro (IV)

Desarrollo de un EA comercial desde cero (Parte 31): Rumbo al futuro (IV)

MetaTrader 5Trading | 18 octubre 2022, 10:04
1 060 0
Daniel Jose
Daniel Jose

Introducción

Después de haber eliminado el Chart Trade dentro del EA, en el artículo Desarrollo de un EA comercial desde cero (Parte 29), hicimos que este mismo Chart Trade se convirtiera en un indicador. La forma de hacerlo, con todos los detalles que conlleva, incluyendo los procedimientos y cuidados necesarios para que el indicador siga siendo útil, se vio en el artículo Desarrollo de un EA comercial desde cero (Parte 30). Esa era uno de las abordajes que podíamos utilizar, y en realidad hay otros con sus ventajas e inconvenientes, pero los veremos en otra ocasión.

Bueno, aún nos quedan más cosas por eliminar dentro del EA, aquí removeremos otra, y la última cosa que realmente se eliminará, en esta serie de artículos, será el sistema de sonido. Tal vez esto los confunda si no han seguido estos artículos.

Para entender cómo se desarrollará todo el proceso, ya que es algo que implica una gran cantidad de cosas que hay que explicar, utilizaremos casi el mismo modelo, basándonos en el artículo anterior, para que la explicación sea lo más sencilla posible de entender, sobre todo, por personas que no son programadores profesionales. No obstante vamos a añadir una pizca de complejidad al sistema, sólo para animar un poco las cosas.

Esta vez la novedad, como ya se ha dicho, será el sistema de sonido. Sí, ahora dejará de formar parte del EA. Pero esto traerá una gran cantidad de ventajas en un futuro próximo. Sin embargo, tomémoslo con calma, porque una perfecta comprensión de lo que va a ocurrir aquí es primordial. A pesar de ello, este artículo será relativamente corto, pero no por ello menos interesante.


Implementación de un servicio de sonido

Puede que se estén volviendo locos con todos estos cambios, pero la idea aquí no es enloquecerlos, quizás un poco aturdirlos 😁, sino que la verdadera intención es mostrar cómo a veces pequeños cambios pueden marcar una gran diferencia y hacer del uso de la plataforma MetaTrader 5 algo mucho más agradable. Al mismo tiempo, podrán notar que ella nos permite modular las cosas.

De esta manera podremos elegir lo que queremos o no, y cuando algo nos guste podremos añadir mejoras a esta función, para hacerla aún más útil y agradable, pero al mismo tiempo, no necesitaremos hacer grandes cambios, ni tendremos que reprogramar cosas que configuramos hace tiempo, la idea es REUTILIZAR SIEMPRE.

Uno de estos recursos es el sistema de sonido, se podría pensar que dejar este sistema dentro del EA es una buena idea. En cierto modo este sistema no entorpece el funcionamiento del EA en general, pero si lo removemos del EA y, en vez de él, mantenemos algún tipo de comunicación con el EA, nos daremos cuenta de que podemos utilizar las alertas sonoras en varios otros momentos y de una manera súper sencilla, como si fuera una librería de sonidos, y esto nos ayudaría mucho.

No tiene sentido que pongamos un sistema de alertas sólo en un EA, puede ser muy útil tener un sistema de alertas sonoras en indicadores o incluso en scripts que se están ejecutando en determinados momentos. Esto nos ayudaría en el análisis, junto con el EA. De este modo, la plataforma MetaTrader 5 puede convertir en un auténtico MONSTRUO en términos de análisis, con una enorme cantidad de cálculos que nos ayudarán a analizar el mercado en momentos muy concretos, ya sea para entrar en posiciones o para cerrarlas. Y esto con el mínimo esfuerzo.

De este modo, creo que muchos pueden estar pensando: Pero puedo añadir en un archivo MQH (Header File) todos los sonidos, incorporarlos en los ejecutables y tener el comportamiento requerido. Sí se puede, pero consideremos el siguiente escenario: con el tiempo, dicho archivo MQH crecerá y, mientras esto sucede, algunos programas antiguos podrán volverse incompatibles con este archivo de cabecera (MQH). Al tener que recompilar esos archivos antiguos, nos encontraremos con problemas, pero si creamos un sistema modular, en el que haya un protocolo de comunicación entre procesos, podremos ampliar la funcionalidad de la plataforma, manteniendo la compatibilidad con programas antiguos.

Este es el propósito y la razón de estos cambios: mostrar cómo se puede crear y utilizar cualquiera de los caminos posibles. Y estoy mostrando eso al tiempo que saco cosas del EA, pero mantengo el funcionamiento lo más parecido al comportamiento original.

En el artículo anterior, mostré cómo recrear el Chart Trade, para que tuviera un comportamiento muy similar al que existía cuando todavía estaba integrado en el EA. Pero al eliminarlo del EA, había que crear alguna forma de que siguiera funcionando de la misma manera. Ese camino que les mostré es uno entre tantos posibles, no es el mejor, pero es uno que funciona. Cada solución requiere un conocimiento adecuado de cómo funcionan las cosas. A veces, limitarnos a un solo modelo de idea no nos ayuda a resolver algunas situaciones concretas, sino todo lo contrario, las empeora. Precisamente por la falta de conocimiento, muchos piensan que las cosas no se pueden hacer o simplemente dicen que el sistema está limitado, cuando en realidad la limitación está en la falta de conocimiento de la persona responsable de planificar y aplicar una solución.

Vimos un ejemplo de esto cuando implementé el sistema de órdenes sin usar ningún tipo de estructura para almacenar los datos, mucha gente pensó que era algo imposible de hacer, que no habría cómo hacer tal cosa, pero les demostré que sí, que es posible hacer tal cosa. El gran detalle es que, para ello, hay que saber y entender lo que se hace, y conocer las limitaciones de cada tipo de solución es el primer camino.

Así que vamos a aprender cómo hacer que el sistema de sonido sea lo más modular posible, considerando que ampliaremos su funcionalidad, ya que el sistema crecerá en el futuro.

En primer lugar, no nos meteremos con él, a menos que la razón sea aumentar la funcionalidad de la clase C_Sound, la cual no cambiará mucho. En realidad, en este punto, esta clase quedará de la misma manera, pero tenemos que hacer algunas pequeñas adiciones al sistema. El primero de ellos es un archivo de cabecera que se ve a continuación:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableAlert "Sound Alert"
//+------------------------------------------------------------------+

Puede que ustedes estén pensando que vamos a utilizar este archivo en el EA, pero no... el EA no utilizará este archivo, al menos no todavía.

Después de esto, podemos crear el archivo que será el servicio de sonido, esto se puede ver en el código de abajo:

#property service
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Auxiliar\C_Sounds.mqh>
#include <NanoEA-SIMD\Interprocess\Sound.mqh>
//+------------------------------------------------------------------+
void OnStart()
{
        union u00
        {
                double value;
                struct s00
                {
                        uint    i0,
                                i1;
                }info;
        }u_local;
        C_Sounds Sound;
        
        while (!IsStopped())
        {
                Sleep(500);
                if (GlobalVariableGet(def_GlobalVariableAlert, u_local.value))
                {
                        GlobalVariableDel(def_GlobalVariableAlert);
                        if (u_local.info.i1 == 0) Sound.PlayAlert((C_Sounds::eTypeSound)u_local.info.i0);
                        else Sound.PlayAlert(u_local.info.i1);
                }
        }
}
//+------------------------------------------------------------------+

Lo que hace este servicio es observar las variables globales de MetaTrader 5, de modo que en cuanto la variable cuyo nombre se declara en el archivo de cabecera es iniciada por cualquier script, EA o indicador, no importa cuál sea ni cuándo suceda, el servicio reproducirá el sonido indicado.

Todo lo que necesitan hacer es indicar el índice de sonido a reproducir, con base en la estructura anterior, podremos reproducir un total de 4 294 967 295 sonidos diferentes, este número es sólo de archivos externos, podemos tener el mismo número de sonidos internos, esto significa que podemos hacer MUCHAS cosas.

Para que el sistema sepa qué tipo de sonido debe reproducir, se verifica el valor de la variable u_local.info.i1, si este valor es 0, el sonido que se reproduzca estará incorporado en el archivo de servicio y el índice del sonido se indicará con la variable u_local.info.i0, pero este valor representa el enumerador dentro de la clase C_Sound.

Con esto ya podemos compilar el servicio y ponerlo en marcha. Y en cuanto se cumplan las condiciones descritas anteriormente, el servicio realizará su trabajo, recordando que cuando la variable global sea capturada por el mismo, será eliminada para ser utilizada en otro momento.

Pero pensemos un momento antes de seguir adelante. A diferencia del indicador Chart Trade, que sólo se comunicará con el EA, el sistema de sonido puede comunicarse con cualquier tipo de programa dentro de la plataforma MetaTrader 5. Y para que se reproduzca el sonido adecuado, tendremos que configurar el valor de la variable, que siempre será un valor double, y tendremos que hacer esto de forma apropiada.

Pueden pensar que esto es simple, pero intenten hacerlo y verán que no es tan simple, además del trabajo de tener que seguir creando la variable global con el nombre correcto todo el tiempo. En otras palabras, como mínimo, tendrás un gran trabajo que hacer cada vez que quieras emitir un sonido previamente almacenado.

Pero hay una solución que es práctica y nos evita todas estas molestias. Como es bastante bella y seductora, la utilizaremos en su nivel más sencillo en este primer momento. Para ver cómo se hace esto, veamos el siguiente tema, que tratará de esta misma cuestión.


Creación de una biblioteca para acceder al servicio de sonido

La razón para crear una biblioteca, es que nos facilitará la vida de alguna manera, no importa cómo, pero nos facilitará la vida. En el tema anterior, dije que cuando un programa accede al servicio de sonido, no necesitaríamos saber el nombre de la variable global que daría acceso al servicio. Y aunque suene extraño, la mejor manera de pasar información entre procesos es añadiendo una capa al sistema, dicha capa es la biblioteca.

Estas bibliotecas "ocultarán" la complejidad del modelado de datos entre procesos, por lo que ya no nos preocupará la forma que debe adoptar este modelado, sino sólo las llamadas en sí mismas y cuál es el resultado esperado.

A la hora de crear una biblioteca sólo tendremos 2 preocupaciones:

  1. Declarar claramente las funciones que se exportarán;
  2. Ocultar la complejidad del modelado interno, tanto como sea posible, de tal forma que el usuario de la biblioteca no necesite saber lo que está pasando, sólo verá los datos que entran y el resultado que sale.

Así que cualquier procedimiento o función dentro de una biblioteca se planea de tal forma que tenga un comportamiento bastante simple, desde el punto de vista del usuario de la función o procedimiento. Pero internamente puede haber un nivel de operación extremadamente complejo hasta que se obtienen los resultados al final. Pero el programador que utiliza la biblioteca no necesita saber lo que ocurre dentro de ella, sino que los resultados se proporcionen correctamente.

Así que vamos a ver nuestra biblioteca, que ocultará el modelado de datos que se utilizará en el servicio de sonido. Todos y cada uno de los programas deberán informar de dos cosas: La primera es si el sonido es interno o externo; la segunda es cuál es el índice del sonido. ¿Suena complicado? Veamos el código de estas llamadas dentro de la biblioteca:

void Sound_WAV(uint index) export { Sound(0, index); }
void Sound_Alert(uint index) export { Sound(index, 0); }

Estas dos funciones se encargan de ocultar cualquier complejidad en el modelado de datos. Observen que estamos usando una palabra clave, export, ésta le dirá al compilador que cree un enlace simbólico a estas funciones. En realidad ellas son procedimientos, ya que no devuelven valores. De esta manera ellas serán visibles fuera del archivo, lo que sería como si este archivo fuera una DLL.

Bueno, pero si miramos todo el código, no encontraremos ninguna función llamada Sound. ¡¿Entonces dónde está?! En realidad está en la propia biblioteca, pero no será visible fuera de ella. Veamos el código completo de la biblioteca a continuación:

//+------------------------------------------------------------------+
#property library
#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Interprocess\Sound.mqh>
//+------------------------------------------------------------------+
void Sound_WAV(uint index) export { Sound(0, index); }
//+------------------------------------------------------------------+
void Sound_Alert(uint index) export { Sound(index, 0); }
//+------------------------------------------------------------------+
void Sound(uint value00, uint value01)
{
        union u00
        {
                double value;
                struct s00
                {
                        uint    i0,
                                i1;
                }info;
        }u_local;
        
        u_local.info.i0 = value00;
        u_local.info.i1 = value01;
        GlobalVariableTemp(def_GlobalVariableAlert);
        GlobalVariableSet(def_GlobalVariableAlert, u_local.value);
}
//+------------------------------------------------------------------+

Tengan en cuenta que el procedimiento Sound contendrá toda la complejidad necesaria para instalar el valor adecuado, para que el servicio realice la tarea que algún script, indicador o EA está tratando de hacer. Pero en lugar de poner este código dentro del programa que accederá al servicio, utilizaremos sólo las llamadas simplificadas, lo que hace que la depuración de los programas sea más cómoda y menos agotadora.

Para que entendamos cómo funciona, veamos un script de ejemplo:

#property copyright "Daniel Jose"
#property script_show_inputs
#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import
//+------------------------------------------------------------------+
input uint value00 = 1;         //Som interno do serviço...
input uint value01 = 10016;     //Som gravado em arquivo WAV...
//+------------------------------------------------------------------+
void OnStart()
{
        Sound_WAV(value01);
        Sound_Alert(value00);
}
//+------------------------------------------------------------------+

Fíjense en el código de arriba del script, no es necesario saber qué tipo de comunicación se está haciendo, dónde y cuándo ocurrirá el evento de reproducción de sonido, puede estar ocurriendo en cualquier lugar, dentro de la plataforma, dentro del sistema operativo o incluso remotamente, no importa. Lo único que tenemos que informar es si el sonido es interno o externo al sistema y, el índice del mismo.

Ahora, antes de continuar, me gustaría que hiciéramos el siguiente experimento: Cambiemos de lugar las funciones, es decir, en la situación actual estamos ejecutando Sound_WAV y luego Sound_Alert. Ejecutémoslo y veamos el resultado. Ahora invertimos el orden, y ejecutamos Sound_Alert, después Sound_WAV, y vemos el resultado. Para aquellos que no entendieron, el código dentro del evento OnStart quedaría así en la primera situación:

void OnStart()
{
        Sound_WAV(value01);
        Sound_Alert(value00);
}

En la segunda situación el código quedaría así:

void OnStart()
{
        Sound_Alert(value00);
        Sound_WAV(value01);
}

Aunque parezca una tontería, este experimento es necesario para que entendamos algunas cosas. No lo ignoren, será interesante que vean los resultados...

Ahora que hemos visto lo que tendremos que añadir a nuestros programas para poder reproducir sonidos, sólo tenemos que añadir el siguiente código:

#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import

Y cuando vayamos a reproducir el sonido, sólo tenemos que utilizar la rutina adecuada con el valor apropiado, sin tener que preocuparnos de cómo se hará realmente la cosa, porque todo el resto del sistema se encargará de que todo funcione perfectamente. En nuestro EA, el código quedará así:

// ...

#import "Service_Sound.ex5"
        void Sound_WAV(uint);
        void Sound_Alert(uint);
#import
//+------------------------------------------------------------------+
#include <NanoEA-SIMD\Trade\Control\C_IndicatorTradeView.mqh>
#include <NanoEA-SIMD\Interprocess\Sound.mqh>

// ...

Pero aquí viene la pregunta: ¿Qué hace el código resaltado ahí? ¿No bastaría con solamente usar la biblioteca? Sí, pero es posible que estemos utilizando una enumeración para identificar el código numérico de los sonidos, como se hacía antes, y a no ser que utilicemos un número muy bajo de sonidos o alertas, saber lo que representa cada uno, sólo mirando el código, es algo muy difícil. Por esta razón, el archivo de cabecera Sound.mqh ha recibido una adición, que está resaltada en el siguiente código:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableAlert "Sound Alert"
//+------------------------------------------------------------------+
enum eTypeSound {TRADE_ALLOWED, OPERATION_BEGIN, OPERATION_END};
//+------------------------------------------------------------------+

De esta manera podemos tener un código como el que se muestra a continuación:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound_Alert(TRADE_ALLOWED);
                return INIT_FAILED;
        }

// ... Restante da função

Él es mucho más representativo que el mismo código, sólo que utiliza índices en lugar de enumeraciones, como se ve justo abajo:

int OnInit()
{
        if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        {
                Sound_Alert(0);
                return INIT_FAILED;
        }

// ... Restante do código

Ahora respondan con sinceridad: ¿¡Qué es más sencillo de entender!?

Al final de todo este trabajo, tendrá un flujo de información dentro de la plataforma, como se muestra en la siguiente figura:

Vean que, independientemente de quién proporcione la señal, siempre tendremos el mismo destino.


Conclusión

Aunque no parezca mucho, lo mostrado en este artículo ayuda mucho a aumentar la usabilidad de nuestro código, programas e información. Como empezamos a programar cada vez menos y nos volvemos cada vez más productivos, nuestro código se vuelve más seguro y estable, porque la reutilización y las pruebas están teniendo lugar en diversos escenarios.

Aquí vimos un camino diferente al visto en el artículo anterior, pero aun así, este puede ser mejorado y mucho, lo que nos puede dar una enorme cantidad de nuevas posibilidades y adaptaciones. Pero eso se verá en otro momento, en otra secuencia que está siendo producida, en ella aprenderemos a cómo hacer que nuestros programas y proyectos de MetaTrader 5 sean mucho más modulares, con un nivel de seguridad, usabilidad y estabilidad muy superior a cualquiera de los métodos mostrados aquí.

Pero lo principal y más importante es que sepamos diseñar y utilizar algunos tipos de solución, porque hay casos en los que una solución será mejor que otra, por una u otra razón.

Todo el código está disponible en el archivo adjunto, para los que no tengan mucha costumbre con esta forma de programar usando librerías. Les aconsejo que estudien bien esta fase de desarrollo del EA, no dejen para mañana lo que puedan hacer hoy, porque mañana puede que no les llegue de la forma que esperan.

Con este artículo, cerramos esta fase del desarrollo del EA. Pronto traeré otro tipo de material, centrado en otro tipo de situación, en la que el nivel de complejidad implicado es mucho mayor, sin embargo bastante más interesante. Así que hasta pronto... Un gran abrazo a todos y hasta pronto...


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

Archivos adjuntos |
EA_-_j_Parte_31_f.zip (14533.52 KB)
Redes neuronales: así de sencillo (Parte 20): Autocodificadores Redes neuronales: así de sencillo (Parte 20): Autocodificadores
Continuamos analizando los algoritmos de aprendizaje no supervisado. El lector podría preguntarse sobre la relevancia de las publicaciones recientes en el tema de las redes neuronales. En este nuevo artículo, retomaremos el uso de las redes neuronales.
Desarrollo de un EA comercial desde cero (Parte 30): ¿¡El CHART TRADE ahora como indicador?! Desarrollo de un EA comercial desde cero (Parte 30): ¿¡El CHART TRADE ahora como indicador?!
Vamos a hacer uso del Chart Trade nuevamente... pero ahora será un indicador y podrá o no estar presente en el gráfico.
DoEasy. Elementos de control (Parte 11): Objetos WinForms: grupos, el objeto WinForms CheckedListBox DoEasy. Elementos de control (Parte 11): Objetos WinForms: grupos, el objeto WinForms CheckedListBox
En este artículo, analizaremos el agrupamiento de objetos WinForms y crearemos una lista de objeto con objetos CheckBox.
Desarrollo de un EA comercial desde cero (Parte 29): Plataforma parlante Desarrollo de un EA comercial desde cero (Parte 29): Plataforma parlante
En este artículo aprenderemos a hacer hablar a la plataforma MT5. ¿Qué tal si hacemos que el EA sea más divertido? Operar en los mercados financieros suele ser una actividad extremadamente aburrida y monótona, pero podemos hacerla un poco menos tediosa. Este proyecto podría ser peligroso en caso de que tengas un problema que te haga adicto, pero en realidad con las modificaciones todo el escenario podría ser más entretenido, menos aburrido.