English Русский 中文 Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Comunicándonos con Meta Trader 5 usando conexiones designadas sin utilizar DLL

Comunicándonos con Meta Trader 5 usando conexiones designadas sin utilizar DLL

MetaTrader 5Ejemplos | 8 mayo 2014, 13:25
1 251 0
MetaQuotes
MetaQuotes

Introducción

Muchos desarrolladores se enfrentan con el mismo problema: cómo llegar al módulo del terminal sin utilizar DLL poco seguras.

Uno de los métodos más sencillos y seguros es utilizar conexiones designadas que funcionan como operaciones de archivo normales. Estos nos permiten organizar la comunicación cliente-servidor entre procesadores entre los programas. Aunque ya hay un artículo publicado sobre este tema (Una solución libre de DLL para la comunicación entre los terminales de Meta Trader 5 usando conexiones designadas) que muestra cómo habilitar el acceso a las DLL, usaremos las características estándar y seguras del terminal de cliente.

Puede encontrar más información sobre las conexiones designadas en la librería MSDN, pero nos iremos a ejemplos prácticos en C++ y MQL5. Implementaremos un servidor, un cliente y un intercambio de datos entre los mismos y luego calificaremos el rendimiento.


Implementación del servidor

Vamos a crear el código de un simple servidor en C++. Un script del terminal conectará con este servidor e intercambiará con datos con él. El núcleo del servidor tiene el siguiente conjunto de funciones de WinAPI:

Una vez que se abre una conexión designada esta devuelve un controlador de archivo que puede usarse para operaciones de lectura/escritura de archivos habituales. Como resultado, tenemos un mecanismo muy simple que no requiere ningún conocimiento especial en operaciones de red.

Las conexiones designadas tienen una característica distintiva: pueden ser locales y de red. Es decir, es fácil implementar un servidor remoto que acepte conexiones de red de los terminales de cliente.

Este es un simple ejemplo de creación de un servidor local como canal full-duplex que funciona en modo de intercambio de bytes:

//--- open 
CPipeManager manager;

if(!manager.Create(L"\\\\.\\pipe\\MQL5.Pipe.Server"))
   return(-1);


//+------------------------------------------------------------------+
//| Create named pipe                                                |
//+------------------------------------------------------------------+
bool CPipeManager::Create(LPCWSTR pipename)
  {
//--- check parameters
   if(!pipename || *pipename==0) return(false);
//--- close old
   Close();
//--- create named pipe
   m_handle=CreateNamedPipe(pipename,PIPE_ACCESS_DUPLEX,
                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                            PIPE_UNLIMITED_INSTANCES,256*1024,256*1024,1000,NULL);

   if(m_handle==INVALID_HANDLE_VALUE)
     {
      wprintf(L"Creating pipe '%s' failed\n",pipename);
      return(false);
     }
//--- ok
   wprintf(L"Pipe '%s' created\n",pipename);
   return(true);
  }

Para obtener una conexión de cliente tenemos que usar la función ConnectNamedPipe:

//+------------------------------------------------------------------+
//| Connect client                                                   |
//+------------------------------------------------------------------+
bool CPipeManager::ConnectClient(void)
  {
//--- pipe exists?
   if(m_handle==INVALID_HANDLE_VALUE) return(false);
//--- connected?
   if(!m_connected)
     {
      //--- connect
      if(ConnectNamedPipe(m_handle,NULL)==0)
        {
         //--- client already connected before ConnectNamedPipe?
         if(GetLastError()!=ERROR_PIPE_CONNECTED)
            return(false);
         //--- ok
        }
      m_connected=true;
     }
//---
   return(true);
  }

El intercambio de datos se organiza usando 4 funciones simples:

  • CPipeManager::Send(void *data,size_t data_size)
  • CPipeManager::Read(void *data,size_t data_size)
  • CPipeManager::SendString(LPCSTR command)
  • CPipeManager::ReadString(LPSTR answer,size_t answer_maxlen)

Estas nos permiten enviar/recibir datos binarios o como cadenas de texto ANSI en modo compatible con MQL5. Además, como CFilePipe en MQL5 abre un archivo en modo ANSI por defecto, los strings son convertidos automáticamente en formato Unicode al recibir y enviar. Si su programa MQL5 abre un archivo en modo Unicode (FILE_UNICODE), entonces puede intercambiar strings Unicode (con la firma inicial BOM).


Implementación del cliente

Escribiremos nuestro cliente en MQL5. Este podrá realizar operaciones de archivo de forma habitual usando la clase CFilePipe de la librería estándar. Esta clase es casi idéntica a CFileBin, pero contiene una importante verificación de la disponibilidad de los datos en un archivo virtual antes de leerlos.

//+------------------------------------------------------------------+
//| Wait for incoming data                                           |
//+------------------------------------------------------------------+
bool CFilePipe::WaitForRead(const ulong size)
  {
//--- check handle and stop flag
   while(m_handle!=INVALID_HANDLE && !IsStopped())
     {
      //--- enough data?
      if(FileSize(m_handle)>=size)
         return(true);
      //--- wait a little
      Sleep(1);
     }
//--- failure
   return(false);
  }

//+------------------------------------------------------------------+
//| Read an array of variables of double type                        |
//+------------------------------------------------------------------+
uint CFilePipe::ReadDoubleArray(double &array[],const int start_item,const int items_count)
  {
//--- calculate size
   uint size=ArraySize(array);
   if(items_count!=WHOLE_ARRAY) size=items_count;
//--- check for data
   if(WaitForRead(size*sizeof(double)))
      return FileReadArray(m_handle,array,start_item,items_count);
//--- failure
   return(0);
  }

Las conexiones designadas tienen diferencias importantes en la implementación de sus modos local y en red. Con dicha verificación, las operaciones en modo de red siempre devolverán un error de lectura al enviar grandes cantidades de datos (más de 64 K).

Vamos a conectar con el servidor con dos comprobaciones: bien a un ordenador remoto llamado 'RemoteServerName' o a una máquina local.

void OnStart()
  {
//--- wait for pipe server
   while(!IsStopped())
     {
      if(ExtPipe.Open("\\\\RemoteServerName\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break;
      if(ExtPipe.Open("\\\\.\\pipe\\MQL5.Pipe.Server",FILE_READ|FILE_WRITE|FILE_BIN)!=INVALID_HANDLE) break;
      Sleep(250);
     }
   Print("Client: pipe opened");


Intercambio de datos

Después de la conexión con éxito vamos a enviar una cadena de texto con la información de identificación al servidor. La cadena de Unicode será convertida de forma automática a ANSI, ya que el archivo se ha abierto en modo ANSI.

//--- send welcome message
   if(!ExtPipe.WriteString(__FILE__+" on MQL5 build "+IntegerToString(__MQ5BUILD__)))
     {
      Print("Client: sending welcome message failed");
      return;
     }

Como respuesta, el servidor enviará su string "Hello from pipe server" y el entero 1234567890. Una vez más el cliente enviará el string "Test string" y el entero 1234567890.

//--- read data from server
   string        str;
   int           value=0;

   if(!ExtPipe.ReadString(str))
     {
      Print("Client: reading string failed");
      return;
     }
   Print("Server: ",str," received");

   if(!ExtPipe.ReadInteger(value))
     {
      Print("Client: reading integer failed");
      return;
     }
   Print("Server: ",value," received");
//--- send data to server
   if(!ExtPipe.WriteString("Test string"))
     {
      Print("Client: sending string failed");
      return;
     }

   if(!ExtPipe.WriteInteger(value))
     {
      Print("Client: sending integer failed");
      return;
     }

Ok, hemos terminado con el intercambio de datos simple. Ahora es el momento de probar el rendimiento.


Prueba del rendimiento

Como prueba que es, enviaremos 1 gigabyte de datos como matriz de tipo doble en bloques de 8 megabytes desde el servidor al cliente, y luego comprobaremos si los bloques son correctos y mediremos el ratio de transferencia.

Este es el código en el servidor de C++:

//--- benchmark
   double  volume=0.0;
   double *buffer=new double[1024*1024];   // 8 Mb

   wprintf(L"Server: start benchmark\n");
   if(buffer)
     {
      //--- fill the buffer
      for(size_t j=0;j<1024*1024;j++)
         buffer[j]=j;
      //--- send 8 Mb * 128 = 1024 Mb to client
      DWORD   ticks=GetTickCount();

      for(size_t i=0;i<128;i++)
        {
         //--- setup guard signatures
         buffer[0]=i;
         buffer[1024*1024-1]=i+1024*1024-1;
         //--- 
         if(!manager.Send(buffer,sizeof(double)*1024*1024))
           {
            wprintf(L"Server: benchmark failed, %d\n",GetLastError());
            break;
           }
         volume+=sizeof(double)*1024*1024;
         wprintf(L".");
        }
      wprintf(L"\n");
      //--- read confirmation
      if(!manager.Read(&value,sizeof(value)) || value!=12345)
         wprintf(L"Server: benchmark confirmation failed\n");
      //--- show statistics
      ticks=GetTickCount()-ticks;
      if(ticks>0)
         wprintf(L"Server: %.0lf Mb sent at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks);
      //---
      delete[] buffer;
     }

y en el cliente MQL5:

//--- benchmark
   double buffer[];
   double volume=0.0;

   if(ArrayResize(buffer,1024*1024,0)==1024*1024)
     {
      uint  ticks=GetTickCount();
      //--- read 8 Mb * 128 = 1024 Mb from server
      for(int i=0;i<128;i++)
        {
         uint items=ExtPipe.ReadDoubleArray(buffer);
         if(items!=1024*1024)
           {
            Print("Client: benchmark failed after ",volume/1024," Kb, ",items," items received");
            break;
           }
         //--- check the data
         if(buffer[0]!=i || buffer[1024*1024-1]!=i+1024*1024-1)
           {
            Print("Client: benchmark invalid content");
            break;
           }
         //---
         volume+=sizeof(double)*1024*1024;
        }
      //--- send confirmation
      value=12345;
      if(!ExtPipe.WriteInteger(value))
         Print("Client: benchmark confirmation failed ");
      //--- show statistics
      ticks=GetTickCount()-ticks;
      if(ticks>0)
         printf("Client: %.0lf Mb received at %.0lf Mb per second\n",volume/1024/1024,volume/1024/ticks);
      //---
      ArrayFree(buffer);
     }

Observe que los elementos primero y último de los bloques transferidos se comprueban para garantizar que no hay errores durante la transferencia. Además, cuando se ha completado la transferencia el cliente envía una señal de confirmación al servidor sobre la correcta recepción de los datos. Si no quiere utilizar confirmaciones finales, encontrará fácilmente una pérdida de datos si una de las partes cierra la conexión demasiado pronto.

Ejecute el servidor PipeServer.exe localmente y adjunte el script PipeClient.mq5 a cualquier gráfico:

PipeServer.exe PipeClient.mq5
MQL5 Pipe Server
Copyright 2012, MetaQuotes Software Corp.
Pipe '\\.\pipe\MQL5.Pipe.Server' created
Client: waiting for connection...
Client: connected as 'PipeClient.mq5 on MQL5 build 705'
Server: send string
Server: send integer
Server: read string
Server: 'Test string' received
Server: read integer
Server: 1234567890 received
Server: start benchmark
......................................................
........
Server: 1024 Mb sent at 2921 Mb per second
PipeClient (EURUSD,H1)  Client: pipe opened
PipeClient (EURUSD,H1)  Server: Hello from pipe server received
PipeClient (EURUSD,H1)  Server: 1234567890 received
PipeClient (EURUSD,H1)  Client: 1024 Mb received at 2921 Mb per second


Para un intercambio local, la velocidad de transferencia es realmente increíble: casi 3 gigabytes por segundo. Esto significa que las conexiones designadas puede utilizarse para trasferir casi cualquier cantidad de datos en los programas de MQL5.

Ahora vamos a medir el rendimiento de transferencia de datos en una red LAN ordinaria de 1 gigabyte:

PipeServer.exe PipeClient.mq5
MQL5 Pipe Server
Copyright 2012, MetaQuotes Software Corp.
Pipe '\\.\pipe\MQL5.Pipe.Server' created
Client: waiting for connection...
Client: connected as 'PipeClient.mq5 on MQL5 build 705'
Server: send string
Server: send integer
Server: read string
Server: 'Test string' received
Server: read integer
Server: 1234567890 received
Server: start benchmark
......................................................
........
Server: 1024 Mb sent at 63 Mb per second
PipeClient (EURUSD,H1)  Client: pipe opened
PipeClient (EURUSD,H1)  Server: Hello from pipe server received
PipeClient (EURUSD,H1)  Server: 1234567890 received
PipeClient (EURUSD,H1)  Client: 1024 Mb received at 63 Mb per second


En una red local, se ha transferido un gigabyte de datos a una velocidad de 63 megabytes por segundo, lo que está muy bien. De hecho, es un 63% del máximo ancho de banda de la red local.


Conclusión

El sistema de protección de la plataforma de trading Meta Trader 5 no permite que los programas MQL5 se ejecuten fuera del sandbox, protegiendo a los operadores frente a amenazas al usar asesores expertos no fiables. El uso de conexiones designadas permite crear integraciones con software de terceras partes con facilidad y gestionar los asesores expertos desde el exterior. De forma segura.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/503

Archivos adjuntos |
pipeclient__3.mq5 (3.15 KB)
pipeserver__3.zip (43.59 KB)
Simulación Rápida de Ideas de Trading en el Gráfico Simulación Rápida de Ideas de Trading en el Gráfico
Este artículo describe el método de simulaciones rápidas virtuales de ideas de trading. El método se basa en la combinación de un gráfico de precio, un indicador de señal y un indicador de cálculo de saldo. Me gustaría compartir mi método de búsqueda de ideas de trading, así como el método que uso para la simulación rápida de estas ideas.
Redes neuronales: de la teoría a la práctica Redes neuronales: de la teoría a la práctica
Hoy en día, cualquier operador ha oído hablar de las redes neuronales y conoce las ventajas de su utilización. La mayoría de ellos creen que quien puede trabajar con redes neuronales es una especie de superman. En este artículo intentaré explicarle la arquitectura de la red neuronal, describir sus aplicaciones y dar ejemplos de su uso práctico.
Señales de Trading en MetaTrader 5: ¡Una Alternativa Mejor a las Cuentas PAMM! Señales de Trading en MetaTrader 5: ¡Una Alternativa Mejor a las Cuentas PAMM!
Nos complace anunciar que MetaTrader 5 ahora cuenta con Trading Signals (Señales de Trading), una poderosa herramienta para inversores y gestores. Mientras sigue las operaciones de un trader con éxito, ¡el terminal las reproducirá automáticamente en su cuenta!
Introducción al método de descomposición de modo empírico Introducción al método de descomposición de modo empírico
Este artículo sirve para familiarizar al lector con el método de descomposición de modo empírico (EMD, según sus siglas en inglés). Es la parte fundamental de la transformada de Hilbert-Huang y tiene como finalidad analizar los datos de procesos no estacionarios y no lineales. Este artículo también presenta una posible implementación de software de este método junto con una breve consideración de sus peculiaridades y proporciona algunos ejemplos simples de su uso.