English Русский 中文 Deutsch 日本語 Português
Web scraping de datos sobre la rentabilidad de los bonos

Web scraping de datos sobre la rentabilidad de los bonos

MetaTrader 5Indicadores | 28 junio 2019, 09:20
1 463 0
Steven Brown
Steven Brown

Introducción

Cuando diseñamos los sistemas del trading automático, casi siempre utilizamos los datos de los indicadores técnicos que analizan el pasado con el fin de predecir el futuro comportamiento del precio. Pero si no tomamos en cuenta las fuerzas fundamentales que mueven el mercado, evidentemente estaremos en una situación menos ventajosa en comparación con los traders que consideran adicionalmente los datos fundamentales en sus decisiones comerciales. El indicador basado en los datos fundamentales recopilados automáticamente puede mejorar el funcionamiento del EA. Probablemente, la información sobre los tipos de interés afecta muchísimo las tasas de cambio de las divisas correspondientes. Generalmente, los tipos de interés de los bancos centrales son menos volátiles que la rentabilidad de los bonos del Estado, como por ejemplo, los bonos fiscales de 10 años en los Estados Unidos. Además, las fluctuaciones en los mercados globales de los bonos se observan en todos los marcos temporales (timeframes). La rentabilidad refleja las expectativas del mercado respecto a los futuros tipos de interés del banco central. La rentabilidad de los bonos a menudo representa un indicador del cambio de los tipos de interés y de las tasas de cambio. En Forex, aplicando a un par de monedas, se analiza la diferencial de los tipos de interés, la delta o el cambio de la diferencial de interés en diferentes timeframes. En la imagen 1 se muestra el caso cuando el movimiento de la diferencial de interés, expresada en puntos base, en la dirección positiva fue un importante indicador del movimiento del para EURUSD en la misma dirección. En este artículo vamos a considerar la manera de recopilar los datos sobre la rentabilidad desde Internet y obtener los valores de la diferencial de interés y la delta a base de estos datos.


Indicador adelantado de la diferencial de los tipos de interés

Fig. 1. Indicador de la diferencial de interés en el gráfico de una hora para EURUSD.

Scraping 101

La página web que se muestra en el navegador normalmente se compone de muchos elementos: texto formateado, gráfica, imágenes, sonido y vídeo. Todos estos elementos se encuentran en los archivos en los servidores web y se cargan consecutivamente por el navegador. Por tanto, para acceder a ellos, se usan determinadas direcciones url. Además, existen los métodos de programa para bajar un elemento de la página con información útil, sin bajar los demás elementos. La obtención de la información de esta manera se llama el scraping (extracción). Para obtener los datos, el programa necesita saber la dirección URL del archivo que contiene el elemento necesario, por ejemplo, un número mostrado en está página. El programa baja este archivo, encuentra el texto con número necesario y lo convierte en un valor numérico.

Cómo obtener la URL

La primera tarea en el scraping consiste en obtener la dirección URL del archivo que contiene el elemento a bajar. Puede ser la dirección URL de la página web si el elemento está incorporado en el texto html en esta página. En este caso, se puede extraer el elemento del texto html. Además, la dirección URL puede estar incorporada en el enlace en la página web. Usando este enlace, el navegador descarga el elemento necesario para visualizarlo en la página web. El programa scraper también puede extraer el enlace del texto html y obtener el elemento necesario. Además, la URL puede pasarse al navegador a través del script cuyo enlace se encuentra en la página. El navegador carga esta página e inicia el script para cargar el elemento. En este caso, el programa scaper no va a ejecutar el script, pero puede usar directamente la dirección URL generada por el script. Además, se puede obtener la dirección URL del elemento usando las herramientas del desarrollador en Internet Explorer o Google Chrome. Cualquiera que sea el origen de la URL, el scaper puede usar esta dirección para cargar el archivo del servidor web y extraer los datos necesarios de él. La información sobre la rentabilidad de los bonos se publica en varios sitios web financieros. Primero, vamos a ver el sitio https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx, y crearemos los ejemplos del scaper a su base.

Primero, vamos a examinar el archivo HTML que el navegador baja del servidor web al pulsar este enlace. En navegador Chrome pulse en el botón de las herramientas en la esquina superior derecha, mueva el cursor en «Herramientas adicionales», seleccione «Guardar página como», descargue el archivo html y ábralo en un editor de texto, por ejemplo, en Notepad. Ahora podemos observar que los robots pueden obtener fácilmente el valor de la cotización ya que está incluida en una de las series de las etiquetas meta en el encabezado del archivo HTML. Los metadatos no se muestran por los navegadores y no influyen en la apariencia de la página. Estos datos están disponibles para los programas que bajan el archivo html. La cotización se encuentra en la etiqueta <meta name="price" content="3.066">, después de 28 caracteres desde el inicio de la etiqueta. Precisamente este valor se muestra en la página en el navegador web. El programa scaper puede encontrar en el archivo la cadena de texto [<meta name="price" content=], contar 28 caracteres desde el inicio de la etiqueta meta y convertir el texto en un número con punto flotante. Para evitar confusiones, en este artículo, se usan los corchetes para el texto html en vez de los paréntesis.

Creando el mejor robot

Si bajamos el archivo html a través del enlace mencionada arriba, podemos observar que contiene grandes bloques de datos de la tabla de estilos, y el tamaño total del archivo es de 295 kb. Mientras que la etiqueta meta que no interesa ocupa sólo 3 kb al principio del archivo. Un robot bueno no debe bajar más datos de los que necesita, por eso, sería lógico bajar sólo los primeros 4 kb al obtener la cotización. Lamentablemente, es imposible limitar el volumen de datos a descargar en la función mql5 WebRequest(). El array con los datos de la respuesta del servidor tiene que ser dinámico. Si se usa un array estático del tamaño especificado, el programa se compilará, pero durante la ejecución, surgirá un error que provocará el bloqueo del terminal. En la solicitud para el servidor, se puede incluir el encabezado Range, pero la mayoría de los servidores no soportan este encabezado. Por eso, a menudo el tamaño de los datos descargados del servidor es casí el mismo que el tamaño del archivo html solicitado. Para esta tarea, la mejor manera es usar las funciones en la biblioteca wininet.dll, un componente de Windows. La función InternetReadFile() de la biblioteca puede cargar una determinada cantidad de los bytes, incluso si el encabezado Range no se soporta y la descarga empieza desde el principio del archivo. Se puede importar las funciones WinINet en el script mql5 o en el EA.. Al artículo se le adjunta el script ScraperBot01.mq5 que descarga los primeros 4 kilobytes del archivo html, determina la posición de la etiqueta meta necesaria en el texto bajado, encuentra en esta etiqueta el texto con la última cotización de la rentabilidad de los bonos de 10 años, lo convierte en un número con punto flotante y muestra el valor obtenido en el terminal.

ScraperBot 01

Al principio del código fuente de ScraperBot01.mq5 se importa la biblioteca wininet.dll y se crean los prototipos de la funciones invocadas. Durante la declaración, los parámetros tienen el tipo compatible con mql5. Puede leer la documentación para las funciones WinINet en la web de Microsoft: https://docs.microsoft.com/en-us/windows/desktop/wininet/wininet-reference.

#import "wininet.dll"
  int InternetCheckConnectionW(string& lpszUrl, uint dwFlags, uint dwReserved);
  int InternetOpenW(string& lpszAgent, uint dwAccessType, string& lpszProxyName, string& lpszProxyBypass, uint dwFlags);
  int InternetOpenUrlW(int hInternetSession, string& lpszUrl, string& lpszHeaders, uint dwHeadersLength, uint dwFlags, uint dwContext);
  int InternetReadFile(int hFile, uchar& lpBuffer[], uint dwNumberOfBytesToRead, uint& lpdwNumberOfBytesRead);
  int InternetCloseHandle(int hInternet);
#import

uchar uc_Buffer[4096]; // для InternetReadFile() ожидается статический буфер.
float f_US;

El búfer estático uc_Buffer que recibe el texto html descargado del servidor, y la variable f_US con el valor de la cotización extraída del texto se declaran a nivel global. En todos los archivos adjuntos al artículo, se utiliza el subrayado entre el especificador del tipo y el nombre para denotar las variables globales. Establecemos el tamaño del búfer uc_Buffer igual al número de los bytes a bajar.

Declaramos algunas variables locales al principio de OnStart(), las demás variables serán declaradas conforme lo necesario para la mayor clareza. Primero, comprobamos la conexión con Internet. Los valores devueltos de las funciones invocadas en este script se muestran en el terminal. Eso permite ver inmediatamente si la función ha funcionado con éxito, y en caso del error los descriptores abiertos se cierran y el trabajo del script se finaliza por el operador return. Si hay conexión con Internet, el descriptor iNet1 se inicializa para las posteriores llamadas a las funciones WinINet. El valor del descriptor tiene que ser más de cero.

void OnStart() 
{ bool bResult;  int i, iNet1, iNet2;  

  string stURL = "http://www.msn.com"; 
  bResult = InternetCheckConnectionW(stURL, 1, 0); // 1 == FLAG_ICC_FORCE_CONNECTION
  Print("InternetCheckConnectionW() returned ", bResult);
  if(!bResult) return;
  
  string stAgent = "Mozilla/5.0", stNull = "";
  iNet1 = InternetOpenW(stAgent, // _In_ LPCTSTR lpszAgent 
                        1,       // 1 == INTERNET_OPEN_TYPE_DIRECT
                        stNull,  // _In_ LPCTSTR lpszProxyName
                        stNull,  // _In_ LPCTSTR lpszProxyBypass
                        NULL);   // _In_ DWORD dwFlags
  Print("iNet1 == ", iNet1);
  if(iNet1==0) return;


Luego, se establece la conexión con el servidor web y se inicializa el descriptor iNet2 para cargar el archivo html.

  stURL = "https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx";
  string stHdr = "Accept: text/*";
  iNet2 = InternetOpenUrlW(iNet1,            // HINTERNET hInternet,
                           stURL,            // LPCWSTR   lpszUrl,
                           stHdr,            // LPCWSTR   lpszHeaders,
                           StringLen(stHdr), // DWORD     dwHeadersLength,
                           0x00080000,       // DWORD     dwFlags, 0x00080000 == INTERNET_FLAG_NO_COOKIES
                           NULL);            // DWORD_PTR dwContext
  Print("iNet2 == ", iNet2);
  if(iNet2==0) 
  { InternetCloseHandle(iNet1);
    return;
  }


Ahora podemos bajar los datos del servidor web.

  uint uGet, uGot;
  uGet = 4080; // número de bytes a bajar
  bResult = InternetReadFile(iNet2,     // _In_  HINTERNET hFile
                             uc_Buffer, // _Out_ LPVOID lpBuffer
                             uGet,      // _In_  DWORD dwNumberOfBytesToRead
                             uGot);     // _Out_ LPDWORD lpdwNumberOfBytesRead

  Print("InternetReadFile() returned ", bResult, ". Number of bytes read: ", uGot);
  InternetCloseHandle(iNet2);  // descarga finalizada
  if(!bResult) {InternetCloseHandle(iNet1); return;}
  uc_Buffer[uGot] = 0// Añadimos cero para terminar la cadena en el búfer uc_Buffer.


Ahora, buscamos la etiqueta meta necesaria en el texto bajado, y si ha sido encontrada, añadimos el desplazamiento en 28 caracteres, será el índice del texto con el valor en el búfer uc_Buffer. Para acceder al texto, llamamos a la función StringSubstr(), pasándole el valor del índice en la variable "i". Si no hay valor necesario en el texto según este índice, la función StringToDouble() devuelve cero, lo que significa un error, salvo lo casos cuando la rentabilidad del bono es cero. Nótese que para los paréntesis en la cadena se usa (\"), para no confundir con los paréntesisal principio y al final de la cadena.

  i = StringFind(CharArrayToString(uc_Buffer), "<meta name=\"price\" content=", 0); // 0 == Posición del inicio de búsqueda
  Print("Offset of \'<meta name=\"price\" content=\' == ", i); 
  if(i == -1) {Print("String not found.");  InternetCloseHandle(iNet1);  return;} 
  i += 28; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_US = StringToDouble(StringSubstr(CharArrayToString(uc_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
  InternetCloseHandle(iNet1); // Ejecutado con wininet.
}//END void OnStart()

Puede ejecutar el script ScraperBot01 en cualquier gráfico, mostrar el progreso de ejecución en el terminal y visualizar el valor obtenido del sitio web.

Una opción alternativa

Hemos conocido el procedimiento general del web scraping de los datos. Ahora, vamos a considerar la opción cuando los datos sobre la rentabilidad no se encuentra en la etiqueta meta. En este caso, examinamos el archivo html de la página web y buscamos el origen del valor visualizado en la página. Sabiendo la última cotización de la rentabilidad del bono, podemos usar la función de la búsqueda en el editor del texto para encontrar la cadena de texto con este valor. En el archivo html descargado, la cotización puede ubicarse en otros tres lugares aparte de la etiqueta meta. El primero de ellos es un fragmento de datos estructurados en el formato JSON-LD. Este elemento permite a los sistemas de búsqueda y a los web crawlers acceder fácilmente a la información sobre la página. A continuación, se muestra este fragmento de datos formateado usando las rupturas de líneas.

<script type="application/ld+json">
{ "@context":"http://schema.org/",
  "@type":"Intangible/FinancialQuote",
  "url":"https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx",
  "name":"U.S. 10 Year Treasury Note",
  "tickerSymbol":"TMUBMUSD10Y",
  "exchange":"Tullett Prebon",
  "price":"3.061",
  "priceChange":"0.007",
  "priceChangePercent":"0.22%",
  "quoteTime":"Sep 28, 2018 5:07 p.m.",
  "priceCurrency":"PERCENT"
}
</script>

El algoritmo primero encuentra el desplazamiento de la etiqueta <script type="application/ld+json">, y luego busca a partir de esta posición el desplazamiento del valor ["price":"] y la etiqueta </script>. Si el desplazamiento ["price":"] es menor que el desplazamiento de la etiqueta </script>, es decir, el valor del precio se encuentra dentro del fragmento de datos, al desplazamiento del texto ["price":"] le añadimos 9 y obtenemos el desplazamiento del valor de la propia cotización. El funcionamiento de este algoritmo se muestra en el script adjunto ScraperBot02.mq5.

ScraperBot 02

El script descarga el archivo html hasta el tamaño especificado en uMax. Durante el primer inicio, es necesario establecer un valor mucho más alto para el parámetro uMax, por ejemplo, 1 millon. El script mostrará el número de los bytes descargados, y si este valor es igual (aunque sea aproximadamente) al parámetro uMax, es necesario aumentar el valor uMax. Además, el script avisará sobre el desplazamiento de la etiqueta <script type=\"application/ld+json\"> en el archivo. Entonces, el valor de este parámetro uMax puede ser definido luego un poco más alto que el desplazamiento de la etiqueta. En nuestro caso, el desplazamiento es 166696, por eso establecemos uMax igual a 180224. Eso nos permitirá bajar la parte suficiente del archivo que contiene el fragmento necesario de JSON-LD, sin tener que descargar el archivo entero. En el script se usa un array estático para descargar los bloques de 16 kb que luego se copian y se acumulan en el array dinámico. Los arrays se declaran a nivel global.

uchar uc_Buffer[16400], uc_DynBuf[];

ScraperBot02 es idéntico al script ScraperBot01 hasta la parte en la que los datos se bajan del servidor web, aquí los datos se dividen en bloques. La función InternetReadFile se invoca en el ciclo do-while hasta que no se baje el volumen necesario de datos.

  uint uGet, uGot, uDst, uMax;
  uGet = 16384;    // el número de bytes para bajar por una llamada a  InternetReadFile, debe ser por lo menos a 1 byte menos que el tamaño de uc_Buffer
  uGot = uDst = 0; // uGot - número de bytes bajados por una llamada a InternetReadFile; uDst es el número total de bytes descargados
  uMax = 180224;   // número máximo de bytes a bajar

  do
  { bResult = InternetReadFile(iNet2,     // _In_  HINTERNET hFile
                               uc_Buffer, // _Out_ LPVOID lpBuffer
                               uGet,      // _In_  DWORD dwNumberOfBytesToRead
                               uGot);     // _Out_ LPDWORD lpdwNumberOfBytesRead

    uc_Buffer[uGot] = 0; // Añadimos cero para terminar la cadena en el búfer uc_Buffer

    ArrayCopy(uc_DynBuf, // array de destino 
              uc_Buffer, // array de origen 
              uDst,      // índice en el array de destino a partir del cual se empieza la escritura 
              0,         // índice en el array de origen a partir del cual se empieza el copiado 
              uGot);     // número de elementos a copiar 
    uDst += uGot; // índice en el array de destino con el desplazamiento para el siguiente paso en el ciclo
  }while(bResult && uGot > 0 && uDst < uMax);
 
  Print("Size of uc_DynBuf == ", ArraySize(uc_DynBuf));
  Print("Bytes downloaded  == ", uDst);

Ahora el script ScraperBot02 encuentra la etiqueta <script type=\"application/ld+json\"> y guarda el desplazamiento como el índice en la variable i. A partir de este desplazamiento, el script encuentra el texto ["price":"] y guarda el desplazamiento en la variable j. Luego, encuentra la etiqueta </script> al final del fragmento y guarda este desplazamiento en la variable k. Si la variable j es menor que k, j se incrementa por 9 y se obtiene el desplazamiento para el texto con el valor de la cotización que se convierte en un valor con punto flotante en la variable f_US y se visualiza en el terminal.

  int i, j, k; // índices

  i = StringFind(CharArrayToString(uc_DynBuf), "<script type=\"application/ld+json\">", 0); // 0 == posición para empezar la búsqueda 
  Print("Offset of <script type=\"application/ld+json\"> == ", i); 
  if(i == -1) {Print("<script type=\"application/ld+json\"> not found.");  InternetCloseHandle(iNet1);  return;}

  j = StringFind(CharArrayToString(uc_DynBuf), "\"price\":\"", i); // i == posición para empezar la búsqueda 
  if(j == -1) {Print("\"price\":\" not found.");  InternetCloseHandle(iNet1);  return;}
  Print("Offset of \"price\":\" == ", j); 

  k = StringFind(CharArrayToString(uc_DynBuf), "</script>", i); // i posición para empezar la búsqueda
  Print("Offset of </script> == ", k); 
  if(j > k) {Print("Offset of \"price\":\" is greater than offset of </script>");  InternetCloseHandle(iNet1);  return;}

  j += 9; // Otro índice para determinar la posición del texto con valor de la rentabilidad.
  f_US = StringToDouble(StringSubstr(CharArrayToString(uc_DynBuf), j, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
  InternetCloseHandle(iNet1); // Ejecutado con wininet.
}//END void OnStart()

Uso de las herramientas del desarrollador

Además, se puede encontrar la fuente de cotización en el servidor web usando las herramientas del desarrollador en el navegador Chrome. Pulse en el botón del menú en la esquina superior derecha de la pantalla, abra las herramientas del desarrollador y especifique https://www.marketwatch.com/investing/bond/tmubmusd10y?countrycode=bx en la barra de dirección. Seleccione la pestaña Network en la parte de arriba, y seleccione "XHR" en el tipo de eventos a monitorear. Al seleccionar cualquiera de eventos, se abre un panel con los detalles en la parte derecha, como encabezados y la respuesta. Nos interesa la respuesta del evento marcado como "quoteByDialect...". Para seleccionarlo, haga clic derecho en el panel y seleccione la opción «Seleccionar todo» Pulse Ctrl+C para copiar el texto seleccionado en Portapapeles, e insértelo en el editor de texto. La cotización se encuentra en el bloque del texto tras la cadena ["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":]. La URL para obtener este bloque se encuentra en la pestaña Headers. La dirección es bastante larga: https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&dialects=Charting&id=Bond-BX-TMUBMUSD10Y,Bond-BX-TMBMKDE-10Y. Pulse en este enlace directamente desde el artículo para ver el bloque del texto en la ventana del navegador. Prácticamente se compone de dos bloques ubicados uno tras otros, porque al final de la dirección hay dos tickers separados por coma. El primero es Bond-BX-TMUBMUSD10Y que representa los bonos fiscales de 10 años en los Estados Unidos. El segundo es Bond-BX-TMBMKDE-10Y que representa los bonos de Estado de 10 años en Alemania. Si eliminamos el segundo ticker de la URL, el tamaño del texto descargado se reduce de 7,1 a 3,6 kb.

El script ScraperBot03 adjunto al artículo descarga el bloque del texto para el ticker "TMUBMUSD10Y", encuentra la cadena ["CompositeTrading":{"Last":{"Price":{"Iso":"PERCENT","Value":], añade el desplazamiento de 61 caracteres desde el principio de la cadena, utiliza eso como el índice de la posición del texto con la cotización, convierte el texto en un número con punto flotante y lo muestra en el terminal. El código del script no se muestra aquí porque ha sido creado a base del script ScrapterBot01. La ventaja de este método consiste en un tamaño pequeño del archivo bajado. Puesto que el tamaño del archivo según la dirección indicada es sólo de 3,6 kb, se puede usar la función mql5 WebRequest para su descarga, en vez de la función de wininet.dll.

ScraperBot 04

Este script descarga los mismos datos que ScraperBot 03, usando la función WebRequest en vez de WinINet. Para que la función WebRequest funcione, hay que añadir la URL principal del servidor (en este caso, "https://api.wsj.net") a la lista de las direcciones permitidas en la plataforma MetaTrader 5, usando el menú «Herramientas» → «Ajustes» → «Asesores Expertos». El array global de los símbolos ch_Data no transmite ningunos datos en WebRequest, es necesario sólo para satisfacer los requerimientos al parámetro de este tipo.

char ch_Buffer[], ch_Data[16];
float f_US;

void OnStart() 
{ int i;   
  string stURL = "https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&"
                 "MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&"
                 "dialects=Charting&id=Bond-BX-TMUBMUSD10Y";
  string stHdr = "Accept: text/*, User-Agent: Mozilla/5.0";
  string stRspHdr; // encabezado de la respuesta
  
  i = WebRequest("GET",     // const string  method, método HTTP 
                 stURL,     // const string  url,       dirección URL
                 stHdr,     // const string  encabezados,  
                 1024,      // int           timeout, 
                 ch_Data,   // const char    &data[],   array del cuerpo de los mensajes HTTP
                 ch_Buffer, // char          &result[], array con datos de la respuesta del servidor
                 stRspHdr); // string        &result_headers 

  Print("Server response code: ", i);
  if(i == -1) {Print("GetLastError == ", GetLastError());  return;}
  Print("Size of ch_Buffer (bytes downloaded) == ", ArraySize(ch_Buffer));
  Print("Response header:\n", stRspHdr);   
 
  string stSearch = "\"CompositeTrading\":{\"Last\":{\"Price\":{\"Iso\":\"PERCENT\",\"Value\":";
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, 0); // 0 == position from which search starts 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_US = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);
}//END void OnStart()


Otros bonos

Para calcular la diferencial porcentual, hacen falta dos tipos de interés. Los scripts que descargan los datos sobre los bonos fiscales de 10 años de los EE.UU. pueden ser editados y se puede obtener los datos sobre la rentabilidad de los bonos de Estado de 10 años de Alemania sustituyendo el nombre de la cotización en las direcciones URL de "TMBMKDE-10Y" por "TMUBMUSD10Y". También se puede usar los tickers para los títulos del Estado de otros países. En otros sitios web financieros, se puede usar otros nombres de los tickers, pero el principio sigue siendo el mismo. Los bonos alemanes a menudo se usan en relación con el tipo de interés, pero no se considera la influencia de otros países de la Unión Europea. Se puede calcular el tipo de interés compuesto de euro a base de los datos sobre los bonos de Estado de dos o más países europeos. En nuestro ejemplo, usamos los datos sobre la rentabilidad de los bonos de Estado de tres países miembros más grandes de la UE (según el PIB) y calculamos el tipo de interés de un «Bono Europeo» compuesta. Países: Alemania, Francia e Italia. Añadimos la ponderación de la rentabilidad de sus bonos de acuerdo con los tamaños relativos de sus economías desde la siguiente tabla.


PIB para 2017, en miles de millones de euros

Alemania 3197
Francia 2241
Italia 1681
Total 7119


El factor de ponderación de cada país será la razón entre su PIB y la suma de tres valores. Obtenemos los siguientes pesos: Alemania = 0,449, Francia = 0,315, Italia = 0,236. La suma de los valores de los pesos es igual a 1, y ellos se utilizan como coeficientes para calcular el valor compuesto de la rentabilidad de las obligaciones europeas que se calcula según esta fórmula:

f_EU = 0.449*f_DE + 0.315*f_FR + 0.236*f_IT

donde f_EU es la rentabilidad de los bonos europeos compuestos, f_DE es la rentabilidad del bono alemán, f_FR es la rentabilidad del bono francés, f_IT es la rentabilidad del bono italiano.

Diferencial de los tipos de interés

La cotización de EURUSD representa el coste del euro expresado en dólares. Por eso, se mueve en la dirección del euro, y por tanto, es opuesto al dólar. Para que la diferencial de los tipos de interés prediga el movimiento del par de divisas en la misma dirección, el crecimiento de la rentabilidad del bono europeo compuesto debe mover la diferencial en la dirección positiva, y el crecimiento del bono fiscal de los EE.UU. debe moverla en dirección negativa. Por eso, la diferencial de los tipos de interés se calcula commo f_EU - f_US. Cuando se escribía este artículo, el valor de f_EU era 1,255, y el valor de f_US era 3,142. De esta manera, la diferencial de los tipos de interés: 1,255 - 3,142 = -1,887. El movimiento en la dirección positiva, digamos de -1,887 a -1,798, prevería el movimiento ascendiente de EURUSD. El movimiento en la dirección negativa, digamos de -1,887 a -1.975, prevería el movimiento descendiente de EURUSD. La fuerza y la confiabilidad del indicador depende del valor del cambio de la diferencial de los tipos de interés. El movimiento de los tipos de interés suele medirse en puntos base o en partes centésimas del por ciento. El movimiento de la diferencial de los tipos de interés de -1,887 a -1,975 significa un movimiento en 8,8 puntos base en dirección negativa, un movimiento bastante fuerte para un rango intradía, lo que probablemente indique en un movimiento descendiente del par de divisas en el timeframe de una hora. Un movimiento de uno o dos puntos base pertenece al área del ruido del mercado y es poso probable que se considere como un indicador de confianza del movimiento del par de divisas.

ScraperBot 05

El script descarga la información sobre la rentabilidad de todos los cuatro bonos, calcula la rentabilidad del bono europeo compuesto y muestra en el terminal la diferencial de los tipos de interés. El script está creado a base del código ScraperBot 04, pero en vez del envío de una solicitud separada sobre cada obligación al servidor, a la URL se le añaden cuatro tickers. Las cuatro cotizaciones se devuelven en un archivo de 14 kb que contiene cusatro bloques del texto. El script ScraperBot 05 encuentra el ticker del símbolo, luego determina el lugar de la cadena que antecede la cotización, y devuelve un error si esta cadena no ha sido encontrada.

char ch_Buffer[], ch_Data[16];      // búferes globales
float f_US, f_DE, f_FR, f_IT, f_EU; // variables globales para guardar los datos sobre la rentabilidad

void OnStart() 
{ int i;   
  string stURL = "https://api.wsj.net/api/dylan/quotes/v2/comp/quoteByDialect?dialect=official&needed=CompositeTrading|BluegrassChannels&"
                 "MaxInstrumentMatches=1&accept=application/json&EntitlementToken=cecc4267a0194af89ca343805a3e57af&ckey=cecc4267a0&"
                 "dialects=Charting&id=Bond-BX-TMUBMUSD10Y,Bond-BX-TMBMKDE-10Y,Bond-BX-TMBMKFR-10Y,Bond-BX-TMBMKIT-10Y"; // cuatro tickers

  string stHdr = "Accept: text/*, User-Agent: Mozilla/5.0";
  string stRspHdr; // encabezado de la respuesta

  i = WebRequest("GET",     // const string  method, método HTTP 
                 stURL,     // const string  url,       dirección URL
                 stHdr,     // const string  encabezados,  
                 1024,      // int           timeout, 
                 ch_Data,   // const char    &data[],   array del cuerpo de los mensajes HTTP
                 ch_Buffer, // char          &result[], array con datos de la respuesta del servidor
                 stRspHdr); // string        &result_headers 

  Print("Server response code: ", i);
  if(i == -1) {Print("GetLastError == ", GetLastError());  return;}
  Print("Size of ch_Buffer (bytes downloaded) == ", ArraySize(ch_Buffer));
   
  string stSearch = "\"CompositeTrading\":{\"Last\":{\"Price\":{\"Iso\":\"PERCENT\",\"Value\":";

// Obtenemos la rentabilidad de los bonos fiscales de 10 años en los EE.UU.
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMUBMUSD10Y\"", 0); // 0 == position from which search starts 
  if(i == -1) {Print("\"Ticker\":\"TMUBMUSD10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); // i == posición para empezar la búsqueda 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_US = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("US 10-year T-note yield, stored in variable f_US: ", f_US);

// Obtenemos la rentabilidad de los bonos de Estado de 10 años en Alemania.
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKDE-10Y\"", i); // i == posición para empezar la búsqueda 
  if(i == -1) {Print("\"Ticker\":\"TMBMKDE-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); // i == posición para empezar la búsqueda 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_DE = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("German 10-year government bond yield, stored in variable f_DE: ", f_DE);

// Obtenemos la rentabilidad de los bonos de Estado de 10 años en Francia.
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKFR-10Y\"", i); // i == posición para empezar la búsqueda 
  if(i == -1) {Print("\"Ticker\":\"TMBMKFR-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); // i == posición para empezar la búsqueda 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_FR = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("French 10-year government bond yield, stored in variable f_FR: ", f_FR);

// Obtenemos la rentabilidad de los bonos de Estado de 10 años en Italia.
  i = StringFind(CharArrayToString(ch_Buffer),"\"Ticker\":\"TMBMKIT-10Y\"", i); // i == posición para empezar la búsqueda 
  if(i == -1) {Print("\"Ticker\":\"TMBMKIT-10Y\" not found.");  return;}
  i = StringFind(CharArrayToString(ch_Buffer), stSearch, i); // i == posición para empezar la búsqueda 
  Print("Offset of ", stSearch, " == ", i); 
  if(i == -1) {Print(stSearch, " not found.");  return;}
  i += 61; // El siguiente índice para determinar la posición del texto con el valor de la rentabilidad.
  f_IT = StringToDouble(StringSubstr(CharArrayToString(ch_Buffer), i, 8));
  Print("Italian 10-year government bond yield, stored in variable f_IT: ", f_IT);

// Obtenemos la rentabilidad de los bonos europeos compuestos.
  f_EU = 0.449*f_DE + 0.315*f_FR + 0.236*f_IT;
  Print("European composite bond yield: ", f_EU);

// Calculate interest rate differential.
  Print("Interest rate differential, f_EU-f_US = ", f_EU-f_US);
}//END void OnStart()

El script ScraperBot06.mq4 implementa el trabajo del script ScraperBot05.mq5 usando la función WinINet en vez de WebRequest, que se considera insegura en MetaTrader 4.


Delta

Los cambios en la diferencial de los tipos de interés tienen mayor importancia para la negociación con los pares de divisas que la propia diferencial. La delta puede ser expresada como un valor de la diferencial de los tipos de interés para el momento del cierre de la barra menos su valor para el momento del cierre de la barra anterior. En los timeframes más largos, se puede usar la delta de un período, y para los timeframes cortos conviene mejor una media móvil exponencial de los valores de la delta que refleja los cambios a lo largo de muchas barras. Para calcular una media móvil suavizada exponencial, el factor medio o la alfa se aplica y se añade al valor anterior de la EMA (se designa como EMAp multiplicada por 1-alfa).

EMA = a*Delta + (1-a)*EMAp

Puesto que la delta puede ser positiva o negativa, y su valor medio es igual a cero, se puede inicializar EMAp igual a cero si el valor anterior de la EMA no está disponible. Se puede establecer el valor de la alfa como aleatorio o calcular a base del número de los períodos de la MA establecido aleatoriamente. En este caso:

a = 2.0 / (n+1)

donde n es el número de los períodos de la EMA que puede ser entero o fraccionado. El rango de la alfa suele ser más de cero, menos o igual a 1, es decir, 0 < a <= 1. Si la alfa es 1, en el cálculo no se considera el valor anterior de la EMA, por eso, la EMA muestra solamente la delta actual.

Si tiene una base de datos del historial de las diferenciales de tipos de interés, el indicador puede mostrar una serie temporal de las diferenciales o la EMA de la delta en el gráfico del par de divisas. Los indicadores no pueden recibir los datos desde Internet, pero pueden obtenerlos desde un archivo ubicado en el disco local cuyos datos se actualizan por el script que extrae los datos sobre la rentabilidad de los bonos. La programación de este indicador es un tema para otro artículo.

Conclusiones

Usted puede copiar el código fuente de los scripts adjuntos a su EA con el fin de automatizar la recopilación de los datos fundamentales. Los métodos de la recopilación de los datos sobre el rendimiento de los bonos desde Internet, demostrados en este artículo, pueden trabajar con otros sitios web financieros y los pares de divisas aparte de los utilizados en los ejemplos. Si el recurso por la dirección URL se cambia o deja de funcionar, el programador puede detectar fácilmente la razón y encontrar la solución. Un robot bueno no debe bajar más datos de los que necesita, y no tiene que enviar las solicitudes con mucha frecuencia (y justamente no en cada tick). La rentabilidad de los bonos es menos volátil que las tasas de cambio de las divisas, por eso, sería lógico actualizar los datos con menos frecuencia que cada cinco minutos. Además, los sitios web con los datos no siempre son de acceso público, por tanto, no tienen que ser redistribuidos. Si se usa de forma correcta, la recopilación automática de estos datos fundamentales puede ayudar a aumentar la rentabilidad del trading automatizado.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/5204

Archivos adjuntos |
ScraperBot01.mq5 (4.23 KB)
ScraperBot02.mq5 (5.66 KB)
ScraperBot03.mq5 (4.54 KB)
ScraperBot04.mq5 (2.59 KB)
ScraperBot05.mq5 (5.02 KB)
ScraperBot06.mq4 (7.48 KB)
Aplicando OLAP en el trading (parte 1): Fundamentos del análisis corriente de datos multidimensionales Aplicando OLAP en el trading (parte 1): Fundamentos del análisis corriente de datos multidimensionales
En este artículo, se describen los principios básicos de la construcción del framework para el procesamiento analítico en línea (OLAP en inglés), su implementación en MQL en el ambiente de MetaTrader, usando el procesamiento del historial de trading de la cuenta como ejemplo.
Utilidad para la selección y navegación en MQL5 y MQL4: aumentando la informatividad de los gráficos Utilidad para la selección y navegación en MQL5 y MQL4: aumentando la informatividad de los gráficos
En este artículo, continuaremos expandiendo la funcionalidad de nuestra utilidad. Esta vez, añadiremos las posibilidades de visualizar la información en el gráfico, que sirve para facilitar nuestra negociación. En particular, añadiremos en el gráfico los precios máximos y mínimos del día anterior, niveles redondos, precios máximos y mínimos durante el año, hora del inicio de la sesión, etc.
Cómo visualizar la historia del comercio multidivisa en informes con formato HTML y CSV Cómo visualizar la historia del comercio multidivisa en informes con formato HTML y CSV
Como sabemos, MetaTrader 5 ofrece la posibilidad de realizar simulaciones multidivisa desde su aparición. Esta función tiene mucha demanda entre la mayoría de los tráders, pero, por desgracia, no es tan universal como querríamos. En el presente artículo, ofrecemos varios programas para trazar gráficos con la ayuda de objetos gráficos usando como base la historia comercial de informes en los formatos HTML y CSV. El comercio con varios instrumentos puede analizarse paralelamente en varias subventanas, o en una sola ventana con la ayuda de la alternancia dinámica a una orden del usuario.
Cómo escribir una biblioteca DLL para MQL5 en 10 minutos (Parte II): Escribiendo en el entorno de Visual Studio 2017 Cómo escribir una biblioteca DLL para MQL5 en 10 minutos (Parte II): Escribiendo en el entorno de Visual Studio 2017
El artículo original «básico» de ningún modo perdió su actualidad, y todos los interesados en este asunto deben leerlo sí o sí. Pero ya pasó bastante tiempo desde aquel entonces, y ahora la versión Visual Studio 2017 es de la actualidad, disponiendo de una interfaz ligeramente modificada, mientras que la propia plataforma MetaTrader 5 tampoco estaba sin desarrollo. El presente artículo describe las etapas de la creación del proyecto DLL, sus configuraciones y el trabajo común con las herramientas del terminal MetaTrader 5.