Elección automática de una empresa de corretaje para un funcionamiento eficiente de los asesores expertos

Shashev Sergei | 11 marzo, 2016


Introducción

Con mucha frecuencia, nos enfrentamos a situaciones en las que un asesor experto opera con éxito con una empresa de corretaje y no es rentable o incluso tiene pérdidas. Las razones son de distinta índole. Cada empresa de corretaje tiene una configuración distinta:

  • Cotizaciones. Difieren ligeramente debido a dos factores: diferentes fuentes de datos y distinto filtrado que ajusta las cotizaciones. Para algunos asesores expertos, esto puede ser importante. Pueden producirse situaciones en las que un asesor experto opere a menudo con una empresa de corretaje y rara vez con otra.
  • Deslizamiento. Puede diferir bastante entre distintas empresas de corretaje. Esto puede generar también peores características de un asesor experto debido al menor beneficio esperado.
  • Recotizaciones. En algunas empresas de corretaje, estas son más frecuentes que en otras. En este caso, un asesor experto perderá los puntos de entrada adecuados debido a un gran número de recotizaciones.

Por tanto, el funcionamiento de algunos asesores expertos depende en gran medida de la empresa de corretaje con la que trabaje. Si no hubiera elección, podríamos tolerar las cotizaciones, el deslizamiento y las recotizaciones. Pero ahora tenemos una alternativa y cada vez es mayor. Por ello, podemos comparar distintas empresas de corretaje, ver sus características técnicas y usando su información podemos elegir las mejores empresas de corretaje para nuestro asesor experto.



Estadísticas

Por supuesto, podemos iniciar varios terminales desde distintas empresas de corretaje, usarlos durante un mes o dos y luego elegir aquella con la que el beneficio sea máximo. Pero dicha prueba es muy poco informativa. Sería mejor obtener más información: deslizamiento medio por transacción, número de recotizaciones que tienen lugar en la apertura de una cierta transacción, momento de la apertura, etc. Para no tener que analizar los registros, se ha desarrollado un proyecto llamado Estadística. Se basa en las siguientes reglas:

  1. Un asesor experto analiza el mercado, realiza transacciones, obtiene todos los datos necesarios sobre las transacciones y los pasa a un módulo común.
  2. Este módulo contiene toda la información relativa a las transacciones actuales y cerradas. También cuenta la estadística sobre todas las características técnicas de las empresas de corretaje.
  3. Debe estar lo más cómodo posible durante la operación con grandes cantidades de datos, de forma que podamos ver solo la información necesaria y no toda la que puede ser recopilada y contabilizada.

Analizando la estadística (número de recotizaciones, momento de la ejecución de la transacción y deslizamiento) y viendo todas las transacciones, podemos decidir finalmente con qué empresa de corretaje es mejor trabajar. Si la estadística sobre todas las empresas es negativa, debemos cambiar algunos parámetros del asesor experto, por ejemplo, el tiempo en el mercado o la frecuencia de las transacciones. Y cambiarlos hasta que el asesor experto comience a trabajar con un beneficio. Si en una etapa concreta un asesor experto deja de obtener beneficios con una empresa de corretaje y es rentable con otras, dejamos de probar esta empresa.



Teoría

Podemos organizar el envío de datos desde un asesor experto a una aplicación mediante archivos o a través de dll. La opción de los archivos es más sencilla en términos de ejecución técnica, ya que no requiere una programación del sistema demasiado compleja. No obstante, no es muy aconsejable trabajar con archivos, ya que no conocemos de antemano dónde estarán los terminales de MetaTrader 4 para las distintas empresas de corretaje, en qué monedas serán probadas, qué hacer si se pierden los archivos, etc. Si necesitamos que todo se realice de forma dinámica, con la máxima seguridad, es mejor organizar los datos pasándolos a través de dll.

Para que los distintos terminales operen con una dll, esta debe ubicarse en el directorio del sistema windows\system32. Es importante que los asesores expertos de un terminar descarguen la misma copia de la dll, ya que todos ellos funcionan dentro de un proceso que el terminal (terminal.exe), lo que significa que tienen el mismo espacio de dirección, es decir, funcionan con las mismas variables. Un terminal con asesores expertos tiene su propia copia de la dll para todos los asesores expertos y las mismas variables, anunciadas dentro de la dll, mientras que otro terminal tiene otra copia con otras variables. De este modo, un terminal no tiene acceso a las variables de otro terminal.

Queremos crear uno único, donde los datos de distintos terminales sean recopilados. Hay distintas formas de organizar un funcionamiento síncrono de distintos procesos con un campo de datos. Esto puede realizarse a través de archivos, pero de nuevo nos encontramos con el problema de indicar la ruta, así como con el de la velocidad de procesamiento si tenemos muchos terminales y asesores expertos. La mejor solución es una memoria del núcleo divisible. Trabajar con esta requiere una mayor atención y conocimientos sobre las características del sistema operativo usado (en nuestro caso Windows), aunque no obstante, las posibilidades disponibles son infinitas. Para la organización de un acceso sucesivo a un cierto bloque de la memoria compartida se utiliza un mecanismo especial de semáforos del programa.


Conclusión sobre la teoría: a través de las dll, los asesores expertos escriben los datos en la memoria compartida y luego, en la aplicación, lo llamamos Monitor, este lee los datos de la memoria, los muestra y realiza los cálculos estadísticos necesarios. Cuando MetaTrader 4 llama a la DLL por primera vez, el sistema operativo genera una copia de esta DLL para cada terminal, ya que cada terminal es un proceso independiente. El esquema de funcionamiento se encuentra en la imagen siguiente.



Práctica

Asesor Experto

Ciertamente, los datos sobre la transacción actual deben ser creados por un asesore experto. Para pasar los datos necesitamos crear la interfaz de la dll de la función. Para la tarea implementada necesitamos tres funciones:

 bool NewExpert(string  isBrokerName, string isInstrument, int Digit);

Crear un nuevo asesor experto identificándolo por el nombre del bróker y el valor bursátil. Para el cálculo de algunas características estadísticas pasamos el número de cifras después de un punto en un precio de un valor bursátil.

bool NewDeal(string isInstrument, int Action, int magik, 
double PriceOpen, int Slippage, int TimeForOpen, int Requotes);

El registro de una nueva transacción se realiza de la forma siguiente. Aunque el proceso del terminal ya está identificado por el nombre del bróker, para una nueva transacción es suficiente el nombre de un valor bursátil sobre el que se ejecuta la misma. Otros parámetros son las características de la transacción.


Tabla 1. Apertura de la transacción

Parámetro

Valor

Acción

0 – buy, 1 - sell

magik

Número mágico

Deslizamiento

Deslizamiento

TimeForOpen

Duración de la apertura

Recotizaciones

Número de recotizaciones recibidas


bool CloseDeal(string isInstrument, int magik, double PriceClose, 
               int Slippage, int TimeForClose, int Requotes);

El cierre de una transacción se identifica con el valor bursátil y el número mágico. Parámetros pasados:

Tabla 2. Cierre de la transacción

Parámetro

Valor

PriceClose

Precio de cierre

Deslizamiento

Deslizamiento

TimeForClose

Duración del cierre

Recotizaciones

Número de recotizaciones recibidas

 

Debido a esta interfaz, la inicialización y apertura y cierre de las funciones tendrá el aspecto siguiente:

Inicialización:

int init()
  {
   int Digit;
   if(IsDllsAllowed() == false)
     {
       Print("Calling from libraries (DLL) is impossible." + 
             " EA cannot be executed.");
       return(0);
     }
   if(!IsTradeAllowed())
     {
       Print("Trade is not permitted!");
       return(0);     
     }
   Digit = MarketInfo(Symbol(), MODE_DIGITS);
   if((Digit > 0) && (Bid > 0))
     {  
       if(!NewExpert(AccountServer(), Symbol(), Digit))
         {
           Print("Creation of a new broker failed");
           return (0);
         }                      
       Print("A broker is successfully created ");                    
       return(0);      
     }   
   Print("No symbol in MarketInfo!");       
   return(0);  
  }

Durante la inicialización, después de comprobar los parámetros del terminal (permiso y confirmación de la transacción de la llamada de la DLL) recibimos la información relativa a los dígitos del valor bursátil y su precio actual. Si ambos parámetros son superiores a cero, el valor bursátil se presenta adecuadamente en el terminal y podemos trabajar con él. Cada bróker se diferencia por su nombre que puede ser recibido usando la función AccountServer(), en base a este nombre los terminales se diferencian entre sí en la memoria compartida. Los asesores expertos se diferencian por el nombre del valor bursátil con el que están operando. Por ello, si se vinculan distintos asesores expertos al mismo par de divisas, estos descargarán la misma copia de una DLL que puede provocar una colisión.

Función de apertura de una nueva orden:

int Deal(int act, double Lot)
  {
   int N = 0;
   int ticket;
   int err;
   double Price_open;
   double Real_price;
   datetime begin_deal;
   double Lots;
   int cmd;
   int magik;
   magik = GenericMagik() + 1;   
   Lots = NormalizeDouble(Lot, 1);
// checking margin for a position opening
   AccountFreeMarginCheck(Symbol(), cmd, Lots);
   err = GetLastError();
   if(err > 0)
     {
       Print("No money for new position");
       return(0);
     }      
   begin_deal=TimeCurrent(); 
   while(N < count)
     {
       if(act == 1)
         {
           Price_open = NormalizeDouble(Ask, Digits);
           cmd = OP_BUY;
         }
       if(act == 2)
         {
           Price_open = NormalizeDouble(Bid, Digits);
           cmd = OP_SELL;
         }
       ticket = OrderSend(Symbol(), cmd, Lots, Price_open,
                          slippage, 0, 0, 0, magik);
       if(ticket > 0)
         {
           if(OrderSelect(ticket, SELECT_BY_TICKET) == true)
             {
               Real_price = OrderOpenPrice();
               NewDeal(Symbol(), cmd,magik, Real_price ,
                       MathAbs(Real_price - Price_open),
                       (TimeCurrent() - begin_deal), N);
             }                   
           return(ticket);
         }
       N++;
       Sleep(5000);
       RefreshRates();
     } 
   return(0);
  }

Una orden se abre mediante la función Deal con dos parámetros: acción (1 - buy, 2 - sell) y lote. Cada orden se diferencia de la anterior en el número mágico: este se incrementa. Una posición intenta abrir en intentos count. La información sobre el número de intentos junto con la duración de la apertura, el precio y el deslizamiento se pasa a la memoria compartida desde la que es leída por el monitor.


Función de cierre de la orden:

bool CloseOrder(int magik)
  {
   int ticket, i;
   double Price_close;
   int count = 0;
   datetime begin_time;
   double Real_close; 
   begin_time = TimeCurrent();    
   for(i = OrdersTotal() - 1; i >= 0; i--) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
           if(OrderSymbol() == Symbol()) 
               if(OrderMagicNumber() == magik)
                 {                  
                   while(count < 10)
                     {                       
                       if(OrderType() == OP_BUY)        
                           Price_close = NormalizeDouble(Bid, Digits);
                       if(OrderType() == OP_SELL)        
                           Price_close = NormalizeDouble(Ask, Digits);
                       if(OrderClose(OrderTicket(), OrderLots(),
                                     Price_close, slippage))
                         { 
                           Real_close = OrderClosePrice();
                           CloseDeal(Symbol(), magik, Real_close,
                                     MathAbs(Real_close - Price_close),
                                     (TimeCurrent() - begin_time), count); 
                           return(true);
                         }
                       count++;
                       Sleep(5000);
                       RefreshRates();
                     }
                 }
     }
   return(false); 
  }

La función de cierre CloseOrder() tiene solo un parámetro de entrada: el número mágico. Una orden intenta cerrar varias veces y este número de intentos será pasado junto con el tiempo de la ejecución de la transacción, precio de cierre y deslizamiento a la memoria y luego se lee por el monitor.

El código restante es el asesor experto probado. Por tanto, para usar Estadística en nuestros propios asesores expertos, necesitamos importar las funciones dll necesarias. Para la inicialización y apertura/cierre de posiciones usamos las funciones Deal y CloseOrder. Si queremos, podemos volver a escribir estas funciones, pero los datos sobre las transacciones deben pasarse de acuerdo con la interfaz contenida en la dll.


A continuación se muestra el ejemplo de implementación de dicho asesor experto usando DLL (no se incluye el código de las funciones indicadas anteriormente).

// Enable dll for operation with monitor
#import "statistik.dll"
  bool NewExpert(string  isBrokerName, string isInstrument, 
                 int Digit);   // Create a broker
  bool NewDeal(string isInstrument, int Action, int magik, 
               double PriceOpen, int Slippage, int TimeForOpen,
               int Requotes);
  bool CloseDeal(string isInstrument, int magik, double PriceClose, 
                 int Slippage, int TimeForClose,
                 int Requotes);
#import
//---- 
extern int Num_Deals = 3;
extern int TimeInMarket = 4;
// maximally acceptable slippage
int  slippage = 10;
// time for rest after a trade
int TimeForSleep = 10;
// period of request
int time_for_action = 1;
// number of attempts for opening a position
int count = 5;
// Function of a new bar
bool isNewBar()
  {
    static datetime BarTime;
    bool res = false; 
    if(BarTime != Time[0]) 
      {
        BarTime = Time[0];  
        res = true;
      } 
   return(res);
  }
//+------------------------------------------------------------------+
//| Generation of magic                                                  |
//+------------------------------------------------------------------+ 
int GenericMagic()
  {
   int deals;
//----  
   for(int i = OrdersTotal() - 1; i >= 0; i--) 
     {
       if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
           if(OrderSymbol() == Symbol())
               if(OrderMagicNumber() != 0)
                   deals++;
     }       
   return (deals);
  }
//+------------------------------------------------------------------+
//| forming signals to open/close a position                         |
//+------------------------------------------------------------------+
int GetAction(int &action, double &lot, int &magic)
   {
    int cnt, total;  
    if(OrdersTotal() <= Num_Deals)
      {
        if(Close[1] > Close[2])
          {
            action = 1;
            lot = 1;
            return(0);
          }
        if(Close[2] < Close[1])
          {
            action = 2;
            lot = 1;         
            return(0);               
          }
      }
    total = OrdersTotal();
    for(cnt = total - 1; cnt >= 0; cnt--)
      {
        if(OrderSelect(cnt, SELECT_BY_POS, MODE_TRADES))
            if(OrderSymbol() == Symbol())  
                if((TimeCurrent() - OrderOpenTime()) > TimeInMarket*60)
                  {
                    action = 3;
                    magic = OrderMagicNumber();
                    return(0); 
                  }
      }
   }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   int action = 0;
   double lot = 1;
   int magic = 0;     
   while(!IsStopped())
     {
       Sleep(time_for_action*1000);      
       RefreshRates();
       if(isNewBar())
         {
           GetAction(action, lot, magic);
           if(((action == 1) || (action == 2)))
             {                                        
               if(IsTradeAllowed())
                   Deal(action, lot);
               Sleep(TimeForSleep*1000);
             }
           if(action == 3)
             {
               if(IsTradeAllowed())
                   if(!CloseOrder(magik))
                     {
                       Print("MANUAL CLOSING OF A POSITION IS NEEDED");
                       Sleep(TimeForSleep*1000);   
                     } 
             }
           action = 0;
           lot = 0;
           magik = 0;
         }
     }
   Print("A serious error occurred, the EA stopped operating");  
   return(0);
  }
//+------------------------------------------------------------------+
El bloque de ejecución del asesor experto es un ciclo infinito sobre la función inicio. En una frecuencia preestablecida time_for_action el asesor experto llama a la función analítica GetAction(), que devuelve por referencia una acción que debe ser realizada por el asesor experto, el lote con el que una posición debe ser abierta y el número mágico en caso de que necesitemos cerrar una posición.

El bloque analítico es aquí elemental: buy, si la barra anterior era superior a la anterior a este, y sell en caso contrario. Las posiciones se cierran por el tiempo. Para probar nuestros propios asesores expertos reescribimos este bloque de acuerdo con su algoritmo. No hacemos ningún cambio en la parte ejecutiva.

DLL

Las DLL pueden ser implementadas en distintos entornos y en diferentes lenguajes. La dll necesaria para nuestro trabajo fue creada en Visual C++. Las transacciones tendrán la estructura siguiente:

struct DealRec
  {
    int Index;
    int Magic;
    int Cur;
    int Broker;
    double PriceOpen;
    double PriceClose;
    int SlipOpen;
    int SlipClose;
    int Action;  // 0 = BUY 1 = SELL
    int TimeForOpen;
    int TimeForClose;
    int ReqOpen;
    int ReqClose;
    int Profit;
    bool Checked; // indication that the position is closed
  };

Estas serán completadas en dos etapas: en la apertura y en el cierre. Es decir, una parte de los datos (precio de apertura, deslizamiento a la apertura, etc.) se pasa en la apertura, y la otra parte (precio de cierre, momento del cierre, etc.) se pasa en el cierre. Los prototipos para llamar a una función en dll

__declspec(dllexport) bool __stdcall NewExpert (char *isBrokerName, 
                                                char *isInstrument, 
                                                int Digit);
__declspec(dllexport) bool __stdcall NewDeal (char *isInstrument, 
                                              int Action, 
                                              int magic, 
                                              double PriceOpen, 
                                              int Slippage, 
                                              int TimeForOpen, 
                                              int Requotes);
__declspec(dllexport) bool __stdcall CloseDeal (char *isInstrument, 
                                                int magic, 
                                                double PriceClose, 
                                                int Slippage, 
                                                int TimeForClose, 
                                                int Requotes);


se diferencia de los prototipos en MQL4 solo en las líneas de paso. Puede revisar la dll fuente, y esto le ayudará en la creación de otros proyectos. Para el proyecto de recompilación abra el archivo statistic.dsw usando el programa Visual C++. El código dll completo está en los archivos statistic.cpp y statistic.h, y el código restante es subsidiario. Todos los archivos indicados están en Statistic.zip.

Monitor

Una herramienta óptima para una escritura rápida de aplicaciones con tablas e interfaz gráfica: una solución de Borland. Se trata de Delphi y C++Builder.

Funciones monitor: crean una memoria compartida, leen los datos de ella y los muestran en tablas, y mantienen las estadísticas de deslizamiento. Hay algunas opciones más que hacen que el trabajo sea más cómodo. Esta es la operativa del monitor:

  1. Mantener el diario de las posiciones abiertas;
  2. Mantener el diario de las posiciones cerradas;
  3. Estadísticas de deslizamiento y recotizaciones;
  4. Tablas ajustables;
  5. Guardar las transacciones en archivos html.

La implementación se encuentra en el archivo zip adjunto StatisticMonitor.zip. Para la recompilación del proyecto usamos el programa C++Builder. La extensión del archivo de proyecto es *.bpr. El código principal está en в main.cpp.



La prueba

Para la prueba, se ha creado un asesor experto especial: tiene las condiciones más simples para entrar y cerrar posiciones por el tiempo (la implementación se mostró anteriormente). El asesor experto con la dll y monitor.exe está en el archivo zip monitor+dll+expert.zip. En el inicio, hacemos clic en START, creando de esta forma una memoria compartida. La DLL debe estar en la carpeta system32. Después de eso, iniciamos varios terminales y adjuntamos el asesor experto a los gráficos de las divisas sobre las que se va a operar. Después de varias transacciones se acumulan las estadísticas. Los datos se recopilan en el monitor/diario. De vez en cuando deben transferirse a un archivo almacenado en forma de página html.


El funcionamiento real de la aplicación será el mismo. Esto permite a los operadores comparar el funcionamiento de distintas empresas de corretaje en términos de sus características técnicas y elegir las mejores para el trading automatizado.



Conclusión

Al usar las dll en MQL4, podemos desarrollar distintos programas que ayudarán no solo a tomar decisiones sobre el trading, sino también a recopilar estadísticas. Lo último puede ser muy útil en el trading y a la hora de elegir una empresa de corretaje. La aplicación creada debe ayudar a los desarrolladores en esta difícil búsqueda. Para analizar los brókeres, adjuntamos statistic.dll a un asesor experto como se describió en el ejemplo analizado en este artículo. Los archivos necesarios para el trabajo se encuentran en monitor+dll+expert.zip. Para la operación copiamos statistic.dll en la carpeta system32, iniciamos Statistic.exe desde cualquier ubicación y abrimos el terminal con los asesores expertos, que descargarán la dll, iniciarán el trading y pasarán sus datos a la memoria compartida. Statistic.exe crea archivos auxiliares y por esta razón es mejor iniciar la aplicación desde una carpeta vacía. Si el programa es interesante para los desarrolladores de robots de trading, este puede modificarse y cambiarse.

Debe recordarse que no todas las empresas de corretaje proporcionan las mismas condiciones para el trading automatizado:

  1. Un bróker puede prohibir el trading automatizado.
  2. Un bróker puede prohibir indicar en la orden colocar SL o TP https://www.mql5.com/ru/forum/103341.
  3. Niveles asimétricos para SL y TP.
  4. Puede no tener opción de una apertura mutua de las órdenes.
  5. Restricción del número de posiciones abiertas simultáneamente en una cuenta. Si el número de órdenes (posiciones abiertas + órdenes pendientes) supera la restricción, la función OrderSend devolverá el código de error ERR_TRADE_TOO_MANY_ORDERS.
  6. Otras restricciones:

Por esta razón, se recomienda encarecidamente leer detenidamente las normas de la empresa de corretaje con la que vamos a trabajar.


El proyecto Statistic muestra los complejos que pueden crearse si añadimos las posibilidades de otros lenguajes y entornos de programación a las opciones de MQL4. El programa creado será útil para los asesores expertos que trabajan con distintas empresas de corretaje, ya que ayuda a analizar sus características técnicas de forma adecuada. Si una empresa de corretaje tiene deslizamientos, las transacciones se ejecutan por tiempo y las recotizaciones son muy frecuentes, luego entonces, ¿para qué necesitamos una empresa de corretaje? ¡Hay muchas alternativas!