English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
Desarrollando un EA comercial desde cero (Parte 15): Acceso a los datos en la web (I)

Desarrollando un EA comercial desde cero (Parte 15): Acceso a los datos en la web (I)

MetaTrader 5Integración | 4 julio 2022, 16:09
408 0
Daniel Jose
Daniel Jose

1.0 - Introducción

La plataforma MetaTrader 5 es la más versátil y completa que uno podría desear tener. A diferencia de lo que dicen muchos desinformados, esta plataforma es extremadamente eficiente y potente, dándonos posibilidades que van mucho más allá de simplemente mirar un gráfico que se está trazando y así operar comprando o vendiendo un determinado activo en un momento dado.

Toda esta potencia viene del hecho de que la plataforma utiliza un lenguaje que prácticamente iguala al más potente que se ha construido hasta la fecha, hablo de C/C++, y la potencia que nos da este lenguaje está muy por encima de lo que los meros operadores sin conocimientos de programación son capaces de realizar o entender.

Cuando estamos operando en el mercado, necesitamos estar de alguna manera conectados a varios temas involucrados en el ámbito global, no podemos limitarnos al gráfico, tenemos que estar de alguna manera conectados a varias otras informaciones igualmente relevantes y que pueden decidir y ser la diferencia entre ganar o perder en una operación.

En la web tenemos varios sitios y lugares en los que una gran y vasta cantidad de información está disponible y accesible para aquellos que saben dónde buscar y cómo utilizar mejor esta información. Y cuanto mejor informado estés, dentro de un plazo adecuado será mejor, pero si vas a utilizar un navegador, el que sea, verás que es muy complicado filtrar bien cierta información, dejándote un montón de pantallas y monitores, y al final aunque la información esté ahí, no puedes aprovecharla bien.

Pero gracias a MQL5, que está muy cerca de C/C++, los programadores de dicho lenguaje podemos hacer algo que va mucho más allá de simplemente operar por medio de un gráfico que se está trazando, podemos buscar, filtrar, analizar datos en la web y así hacer operaciones de una forma mucho más consistente de lo que la mayoría de los operadores consiguen, ya que vamos a utilizar toda la potencia de cálculo a nuestro favor.


2.0 - Planificación

La parte de la planificación es algo crucial, primero debemos buscar de dónde vamos a sacar la información que realmente vamos a utilizar y hay que hacerlo con mucho más cuidado de lo que parece, ya que una buena fuente de información nos dará una orientación correcta. Esta parte es algo que debe hacer cada uno y de forma individual, puesto que cada uno estará buscando y necesitando datos específicos en diferentes momentos.

Pero, independientemente de esto, el método para hacer la cosa, una vez elegida la fuente, será básicamente el mismo, por lo que este artículo puede servir como fuente de estudio para aquellos que deseen utilizar el método y los medios que son posibles sólo usando MQL5, sin ningún programa externo.

Para ejemplificar todo el proceso, utilizaremos una página web de información de mercado para mostrar cómo se produce el proceso, de esta manera creo que quedará muy claro cómo hacer que todo funcione, y así podrás utilizar el método adaptándolo a tus necesidades particulares.


2.0.1 - Desarrollo del programa de captura

Para empezar de hecho, primero tendremos que elaborar un pequeño programa para capturar los datos y poder analizarlos con calma y precisión, para ello utilizaremos el programa más sencillo posible, se ve a continuación:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnStart()
{
        Print(GetDataURL("https://tradingeconomics.com/stocks"));
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout = 750)
{
	string headers;
	char   post[], charResultPage[];
	int    handle;
   
	if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1		return "Bad return";
	if ((handle = FileOpen("url.txt", FILE_WRITE | FILE_BIN)) != INVALID_HANDLE) 
        { 
                FileWriteArray(handle, charResultPage, 0, ArraySize(charResultPage)); 
                FileClose(handle); 
        }else
                return "Error saving file ...";
                
	return "File saved successfully...";
}

Este programa es ultra simple, me parece imposible de hacer algo más simple.

Bueno lo que hacemos es indicar en el punto resaltado cual es el sitio del que queremos adquirir la información, ¡¿y por qué estamos usando este y no el navegador?! Es cierto que sí podemos capturar la información en el navegador, pero la utilizaremos para ayudarnos a encontrar la información después de descargar los datos.

Pero no sirve de nada teclear y compilar este programa, hay que hacer algo más, de lo contrario no funcionará.

Dentro de la plataforma MetaTrader, antes de ejecutar este script, debemos habilitar la plataforma para que reciba los datos del sitio en cuestión, pero para evitar tener que hacer esto cada vez que necesites instalar la plataforma MetaTrader, puedes, después de que todo esté configurado, guardar una copia de seguridad de estos datos, el archivo que debes guardar está en la siguiente ruta:

C:\Users\< USER NAME >\AppData\Roaming\MetaQuotes\Terminal\< CODE PERSONAL >\config\common.ini

USER NAME es su nombre de usuario dentro del sistema operativo, CODE PERSONAL es un valor que la plataforma creará durante la instalación, de esta manera le será fácil encontrar el archivo necesario para hacer una copia de seguridad del mismo o para reemplazarlo después de una nueva instalación. Un detalle: esta ubicación se refiere al sistema WINDOWS.

Bien, volviendo al script que hemos creado, si lo utilizas antes de configurar las cosas obtendrás el siguiente resultado en la ventana de mensajes.

Este mensaje se produjo debido a la falta de habilitación del sitio dentro de la plataforma MetaTrader, esto debe hacerse como se muestra en la siguiente figura, observa cuidadosamente lo que se añadió y ten en cuenta que es exactamente la dirección de la raíz del sitio al que queremos acceder a través de la plataforma MetaTrader.

          

Ahora, cuando volvemos a ejecutar el mismo script, obtenemos el siguiente resultado, que es informado por la plataforma:

Es decir, se accedió al sitio y los datos se descargaron exitosamente en nuestra computadora y ya podemos analizarlos, el gran detalle es que ahora no hay que preocuparse por volver a agregar el mismo sitio a la plataforma, siempre y cuando hagamos un respaldo del archivo que mostré donde está.

Para entender cómo funcionarán las cosas aquí y así obtener más detalles, echa un vistazo a la función WebRequest en la documentación, después de todo si quieres profundizar en este campo de la comunicación a través del protocolo de red, te aconsejo que eches un vistazo a las otras funciones de red presentes en MQL5, el conocimiento adecuado de dichas funciones puede sacarte de grandes problemas en ocasiones.

Bueno la primera parte del trabajo está hecha, conseguir descargar los datos del sitio deseado, ahora tenemos que pasar por el siguiente paso, igualmente importante.


2.0.2 - Búsqueda de datos

Para aquellos que no sepan cómo buscar datos para ser capturados por la plataforma MetaTrader 5 dentro de un sitio web, he realizado un breve vídeo donde demuestro de forma muy rápida cómo proceder a esta búsqueda.

Es importante que sepas manejar tu navegador para poder analizar el código del sitio del que quieres capturar los datos, no es difícil, ya que el propio navegador ayuda mucho en esta tarea, pero es algo que debes aprender a hacer, y cuando entiendas cómo hacerlo, se abrirá un gran abanico frente a ti.

En este caso, usaré Chrome para hacer la búsqueda, pero puedes usar cualquier otro navegador que te permita acceder al código a través de las herramientas de desarrollo.



Una vez que sepas qué buscar, dónde buscar y cómo acceder a la información que quieres capturar, es el momento de abrir el archivo que has descargado con la plataforma MetaTrader y empezar a buscar los datos que necesitas.

Lo que nos interesa es obtener los datos de este bloque que se muestra a continuación, y este bloque es el mismo que busqué en el video de arriba. Vean que es importante saber buscar cosas usando el navegador, de lo contrario estarían perdidos en medio de toda esa información descargada.

Bien, pero en algunos casos con mirar los datos de esta manera no será suficiente, tenemos que recurrir a un editor hexadecimal para saber exactamente con qué estamos tratando, es cierto que en algunos casos el modelado de los datos es relativamente sencillo, pero en otros la cosa es bastante compleja, conteniendo imágenes y enlaces entre otras cosas, y estos pueden entorpecer el sistema de búsqueda, ya que suele dar falsos positivos, por lo que tenemos que saber con qué estamos tratando, así que al buscar estos mismos datos en un editor hexadecimal, obtenemos los siguientes valores.

Los offsets no nos interesan, en este primer momento, ya que pueden cambiar en casos de páginas dinámicas, pero lo que nos interesa es ver qué modelado se utiliza, y en este caso está muy claro y podemos utilizar un sistema de búsqueda basado en este tipo de información que se encuentra aquí en el editor hexadecimal. Esto hace que la búsqueda por parte de nuestro programa sea algo más sencilla de implementar, aunque no sea un sistema eficiente en este primer momento. Buscaremos una base de datos más sencilla a la que se pueda acceder, y que se pueda informar a través de input, es decir, sin caracteres como CARRIAGE o RETURN, estos nos estorban más de lo que nos ayudan, entonces el programa queda como se puede ver a continuación.

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnStart()
{
        while (!IsStopped())
        {
                Print(GetDataURL("https://tradingeconomics.com/stocks"));
                Sleep(200);
        }
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout = 100)
{
        string  headers, szInfo;
        char    post[], charResultPage[];
        int     handle;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1)
                return "Bad return";
   
        szInfo = "";
        for (int c0 = 0, c1 = ArraySize(charResultPage); c0 < c1; c0++) szInfo += CharToString(charResultPage[c0]);
        if ((handle = StringFind(szInfo, "data-symbol=\"INDU:IND\"", 0)) >= 0)
        {
                handle = StringFind(szInfo, "<td id=\"p\" class=\"datatable-item\">", handle);
                for(; charResultPage[handle] != 0x0A; handle++);
                for(handle++; charResultPage[handle] != 0x0A; handle++);
                szInfo = "";
                for(handle++; charResultPage[handle] == 0x20; handle++);
                for(; (charResultPage[handle] != 0x0D) && (charResultPage[handle] != 0x20); handle++) szInfo += CharToString(charResultPage[handle]);
        }
        return szInfo;
}

La idea del script de arriba es capturar el valor en la página, la ventaja del método mostrado arriba es que aunque la información cambie de posición, es decir de desplazamiento, seguimos teniendo la posibilidad de encontrarla en medio de todos esos comandos, pero aunque todo parezca ideal, hay un pequeño retraso en la información, por lo que es necesario medir cómo se va a trabajar con los datos capturados, cuando se ejecuta el script de arriba, el resultado de la ejecución se puede ver a continuación.

Te aconsejo que hagas tu propio análisis y veas cómo se está capturando la información, porque es importante conocer detalles que no son tan sencillos de describir en forma de texto, hay que ver para entender.

Bueno, pero pensemos en lo siguiente, el script de arriba no es muy eficiente en cuanto a la ejecución, ya que hace algunas manipulaciones que en realidad no necesitamos en el caso de estar utilizando una página con un modelado estático, pero con contenido dinámico, como es el caso de la página en cuestión. En este caso específico podemos utilizar el offset para hacer un análisis más rápido y así capturar los datos un poco más eficientemente, si es que puedo decir esto, pero recuerda que el sistema puede mantener la información en caché por unos momentos, entonces la información que se está capturando puede estar desfasada con respecto a la misma información observada en un navegador, en este caso es necesario hacer algunos ajustes internos dentro del sistema para poder corregir esto, pero este no es el propósito de este artículo.

Así que modificando el script anterior a algo que utilice el offset para hacer la búsqueda tendremos el siguiente código, y esto es en su totalidad:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnStart()
{
        while (!IsStopped())
        {
                Print(GetDataURL("https://tradingeconomics.com/stocks", 100, "INDU:IND", 172783, 173474, 0x0D));
                Sleep(200);
        }
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout, const string szFind, int iPos, int iInfo, char cLimit)
{
        string  headers, szInfo = "";
        char    post[], charResultPage[];
        int     counter;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1)
	        return "Bad return";

        for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error in Position";
        for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++);
        for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return szInfo;
}

y el resultado de la ejecución del script se puede ver a continuación, veamos que no tenemos grandes cambios, es sólo una cuestión de tiempo de cálculo, que estamos reduciendo utilizando el modelo de offset y por lo tanto mejoramos ligeramente el rendimiento general del sistema.

Recordemos lo siguiente, el código anterior solo funcionaba porque la página tenía un modelado estático, es decir, a pesar de que el contenido cambiaba dinámicamente, el diseño de la misma no cambiaba, por lo que podemos utilizar un editor hexadecimal, buscar donde está la información, obtener los valores de offset e ir directamente a estas posiciones, pero para tener un mínimo de seguridad de que los offsets siguen siendo válidos, hacemos una simple prueba y esto se hace en la siguiente línea:

for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0]) return "Error in Position";

algo muy sencillo, pero necesario, para que tengamos el mínimo de seguridad en cuanto a la información que se está capturando vía offset. Para ello hay que analizar la página y verificar si es o no viable utilizar el método offset para realizar la captura, pero siendo viable se tendrá el beneficio de tener un menor tiempo de procesamiento.


2.0.3 Un problema a gestionar

Aunque a menudo el sistema funciona muy bien, puede ocurrir que recibamos la siguiente respuesta del servidor:

Este mensaje es publicado por el servidor como retorno de nuestra petición, aunque la función WebRequest no indica ningún error, y esto por el lado de la plataforma, el servidor devuelve este mensaje por encima, por lo que tenemos que analizar la cabecera del mensaje de retorno, para que no seamos rehenes de este tipo de problema, entonces para solucionar esto hacemos un pequeño cambio en el script de offset, esto se puede ver a continuación:

#property copyright "Daniel Jose"
#property version   "1.00"
//+------------------------------------------------------------------+
void OnStart()
{
        while (!IsStopped())
        {
                Print(GetDataURL("https://tradingeconomics.com/stocks", 100, "<!doctype html>", 2, "INDU:IND", 172783, 173474, 0x0D));
                Sleep(200);
        }
}
//+------------------------------------------------------------------+
string GetDataURL(const string url, const int timeout, const string szTest, int iTest, const string szFind, int iPos, int iInfo, char cLimit)
{
        string  headers, szInfo = "";
        char    post[], charResultPage[];
        int     counter;
   
        if (WebRequest("GET", url, NULL, NULL, timeout, post, 0, charResultPage, headers) == -1
                return "Bad";
        for (int c0 = 0, c1 = StringLen(szTest); c0 < c1; c0++) if (szTest[c0] != charResultPage[iTest + c0])
                return "Failed";
        for (int c0 = 0, c1 = StringLen(szFind); c0 < c1; c0++) if (szFind[c0] != charResultPage[iPos + c0])
                return "Error";
        for (counter = 0; charResultPage[counter + iInfo] == 0x20; counter++);
        for (;charResultPage[counter + iInfo] != cLimit; counter++) szInfo += CharToString(charResultPage[counter + iInfo]);
        
        return szInfo;

La línea resaltada es exactamente la que hace la prueba, ya que cuando el mensaje de retorno del servidor es un mensaje más complejo, el simple hecho de hacer esta prueba ya nos garantiza un buen margen de seguridad sobre los datos que estamos analizando, y evita el análisis de datos fantasmas, o basura de memoria en caso de que el sistema pase por la primera prueba que ya existía en el código anterior. Aunque es poco frecuente, no debemos subestimar la posibilidad de que esto ocurra.

Bueno, el resultado no es diferente, esto se puede ver a continuación, por lo que el sistema está funcionando como se esperaba.


Bueno, pero hasta ahora no hemos hecho grandes cosas, sólo estamos leyendo valores de una página web, y esto no tiene gran utilidad, aunque es algo bastante curioso de aprender y ver cómo se hace, no tiene mucha utilidad práctica para los que realmente quieren operar y comerciar en base a la información, que a partir de este momento vas a modelar, ya que se capturan y se muestran de una manera diferente, por lo que tenemos que hacer que esto comience a tener algún sentido dentro de un sistema más amplio, y en este caso vamos a tomar esta información capturada dentro de un EA, de esta manera se pueden hacer cosas aún más impresionantes y que de hecho hace que la plataforma MetaTrader 5 sea una plataforma sensacional.


Conclusión

Pues ahí no acaba la cosa, en el próximo artículo les mostraré cómo llevar esta información recogida de la web al EA, y esto nos sacará ciertamente de dudas, ya que será algo mucho más interesante, pues tendremos que utilizar recursos poco explorados dentro de la plataforma MetaTrader, así que no te pierdas el próximo artículo de la serie.

En el anexo están todos los códigos fuente utilizados en este artículo.


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

Archivos adjuntos |
Desarrollando un EA de trading desde cero (Parte 16): Acceso a los datos en la Web (II) Desarrollando un EA de trading desde cero (Parte 16): Acceso a los datos en la Web (II)
Saber cómo introducir los datos de la Web en un EA no es tan obvio, o mejor dicho, no es tan simple que puede hacerse sin conocer y entender realmente todas las características que están presentes en MetaTrader 5.
DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control "Panel", parámetro AutoSize DoEasy. Elementos de control (Parte 5): Objeto básico WinForms, control "Panel", parámetro AutoSize
En este artículo, crearemos un objeto básico para todos los objetos de la biblioteca WinForms y comenzaremos a implementar la propiedad AutoSize del objeto WinForms "Panel", es decir, el cambio automático del tamaño para que se ajuste a su contenido interno.
Desarrollando un EA comercial desde cero (Parte 17): Acceso a los datos en la web (III) Desarrollando un EA comercial desde cero (Parte 17): Acceso a los datos en la web (III)
En este artículo continuaremos a aprender cómo obtener datos de la web para utilizarlos en un EA. Así que pongamos manos a la obra, o más bien a empezar a codificar un sistema alternativo.
Desarrollando un EA comercial desde cero (Parte 14): Volume at Price (II) Desarrollando un EA comercial desde cero (Parte 14): Volume at Price (II)
Hoy añadiremos varios recursos a nuestro EA. Este artículo les resultará bastante interesante y puede orientarlos hacia nuevas ideas y métodos para presentar la información y, al mismo tiempo, corregir pequeños fallos en sus proyectos.