Métodos de control remoto de EAs

25 diciembre 2018, 08:54
Dmitriy Gizlyk
0
556

Contenido

Introducción

En los tiempos de las tecnologías de la información, el uso de distintos robots y expertos electrónicos para comerciar en los mercados financieros se ha convertido en un hecho bastante común. Normalmente, se consideran como principales ventajas de los expertos electrónicos la impecable ejecución del algoritmo y su funcionamiento ininterrumpido las 24 horas del día. Para usarlo las 24 horas, se alquilan las capacidades de los hostings virtuales, lo que permite utilizar asesores de forma independiente durante todo el día.

Pero, desafortunadamente, no todos los asesores funcionan igual de bien en cualquier situación del mercado. En semejantes casos, se debe controlar manualmente el funcionamiento de los asesores: bien activándolos, bien desactivándolos. Esto se puede hacer fácilmente cuando el usuario está cerca del terminal. Pero, ¿qué ocurre si no se dispone de acceso rápido al terminal con el asesor en funcionamiento? En este caso, estaría bien poder controlar de forma remota el funcionamiento del asesor. Vamos a analizar uno de los posibles métodos de control remoto mediante asesores expertos en el terminal.

1. Formulando la tarea

A primera vista, la tarea parece clara: debemos crear un programa que dé órdenes a los expertos al recibir ciertos comandos externos. Pero, tras analizar el tema más a fondo, entendemos que en MetaTrader 5 no existe la posibilidad de ejercer una influencia programática directa en el funcionamiento de un asesor externo. Cada asesor trabaja en una secuencia separada y no hay posibilidad de determinar la presencia de un asesor iniciado como sucede con cualquiera de los gráficos abiertos. La solución a esta cuestión fue propuesta por el usuario fxsaber en la biblioteca "Expert - biblioteca para MetaTrader 5".

En este código, el autor propone aprovechar la oportunidad de guardar plantillas. A primera vista, guardar plantillas no influye en el funcionamiento de los programas que ya se ejecutan en el gráfico. Y claro que esto es así. Pero al guardar una plantilla del gráfico, se registran en un archivo todos los objetos gráficos existentes en el gráfico, así como todos los programas que se ejecutan en el gráfico con sus respectivos parámetros. El posterior uso de la plantilla guardada en el gráfico permite al usuario restaurar en el gráfico todos los objetos y programas gráficos con los parámetros guardados en el archivo.

La segunda consecuencia de esta influencia es la eliminación completa de todos los objetos y programas del gráfico existes antes de que se cargara la plantilla. Dicho de otra forma, si se ha iniciado un asesor en el gráfico y en la plantilla cargada no hay ningún asesor, dicho asesor será eliminado del gráfico y viceversa. Como resultado de la solución propuesta, el trabajo de carga y elimininación del asesor experto de un gráfico se reduce a la edición del archivo de plantilla.

Por supuesto, podemos preparar las plantillas necesarias de antemano, y luego cargar las que queramos con el comando sin tener que editarlas. Pero con este enfoque, el número de plantillas requeridas aumenta dos veces más rápido que el número de asesores utilizados. Además, si se usan configuraciones del asesor diferentes para instrumentos distintos, la adición de cada instrumento de trabajo aumentará aún más el número de plantillas. En tal caso, solo la preparación de plantillas se convertirá ya en un trabajo de rutina, y en el futuro será necesario no confundirse en su aplicación.

La edición de plantillas también tiene sus propios matices. Por defecto, las plantillas se guardan en un directorio especial "catálogo_de_datos\Profiles\Templates\". Pero MQL5 permite trabajar solo con los archivos del sandbox. La solución a este problema también fue encontrada por fxsaber, que sugirió añadir una ruta al sanbox al especificar el nombre del archivo de la plantilla. Como resultado de ello, se logró acceder al archivo de plantilla sin utilizar bibliotecas de terceros.

Agradecemos al usuario fxsaber su perspicacia y capacidad de pensar fuera de los marcos ordinarios.

Después de decidir los métodos de control de los asesores, debemos pensar en el modelo de envío de mensajes entre el programa y el usuario. Hoy en día, las personas están obligadas a ser móviles y, a menudo, se encuentran lejos de su computadora, pero su smartphone está casi siempre a mano. Tanto para iPhone como para Android, existen aplicaciones del terminal MetaTrader 5. Conectando un terminal de este tipo a su cuenta, el usuario podrá analizar el movimiento de los precios en los gráficos y realizar operaciones manuales. Pero, desafortunadamente, hoy en día los terminales móviles no permiten el uso de asesores electrónicos e indicadores de terceros. Por lo tanto, para trabajar con asesores electrónicos se usan más las versiones de escritorio del terminal ejecutadas en la computadora del usuario o, más a menudo, en un hosting virtual.

El terminal móvil conectado a la cuenta nos permite ver una imagen de la cuenta, pero no hay canales de conexión directa entre los terminales móviles y el de escritorio. En lo único que podemos influir es en la colocación y eliminación de órdenes. Las órdenes colocadas se procesan de inmediato en la cuenta y pueden ser monitoreadas por el asesor iniciado en el terminal de escritorio. De este modo, junto con la colocación de órdenes, podemos transferir comandos de control a nuestro asesor-maestro. Solo queda por definir la lista de comandos y qué código transmitirles. Analizaremos estos temas con más detalle en los siguientes capítulos.

2. Analizando el archivo de la plantilla

Para empezar, proponemos analizar la estructura del archivo de plantilla. Más abajo se presenta un ejemplo con la plantilla del gráfico EURUSD M30 con el asesor colocado ExpertMACD y el indicador MACD.

<chart>
id=130874249669663027
symbol=EURUSD
period_type=0
period_size=30
digits=5
.....
.....
windows_total=2

<expert>
name=ExpertMACD
path=Experts\Advisors\ExpertMACD.ex5
expertmode=0
<inputs>
Inp_Expert_Title=ExpertMACD
Inp_Signal_MACD_PeriodFast=12
Inp_Signal_MACD_PeriodSlow=24
Inp_Signal_MACD_PeriodSignal=9
Inp_Signal_MACD_TakeProfit=50
Inp_Signal_MACD_StopLoss=20
</inputs>
</expert>

<window>
height=162.766545
objects=103

<indicator>
name=Main
........
</indicator>
<object>
.......
</object>

<object>
........
</object>
........
........
<object>
........
</object>

</window>

<window>
height=50.000000
objects=0

<indicator>
name=MACD
path=
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=-0.001895
scale_fix_max=0
scale_fix_max_val=0.001374
expertmode=0
fixed_height=-1

<graph>
name=
draw=2
style=0
width=1
color=12632256
</graph>

<graph>
name=
draw=1
style=2
width=1
color=255
</graph>
fast_ema=12
slow_ema=24
macd_sma=9
</indicator>
</window>
</chart>

Como se puede ver en el código mostrado, la información en el archivo de plantilla está estructurada y dividida por tags. El archivo comienza con el tag <chart> con la descripción de la información principal sobre el gráfico, donde usted puede encontrar el identificador del gráfico, su instrumento y su marco temporal. La información que nos interesa sobre el experto, se encuentra entre los tags <expert> y </expert>.

Al comienzo del bloque se muestra información sobre el experto: su nombre breve representado en el gráfico, y la ruta al archivo. A continuación, viene la bandera expertmode, su estado indica el permiso conferido al asesor para realizar operaciones comerciales. Después de ello, encontramos los parámetros del asesor, destacados con los tags <inputs> </inputs>. Después va la información sobre las subventanas del gráfico. Cada subventana se destaca con los tags <window> y </window>, entre los cuales se describen los indicadores iniciados (destacados con los tags <indicator> ... </indicator>) y los objetos colocados en el gráfico (destacados con los tags <object> ... </object>).

También se debe tener en cuenta que si un asesor experto y/o un indicador creado en el cuadro crea objetos gráficos, estos deben eliminarse de la plantilla. Puesto que, en caso contrario, al fijar una plantilla al gráfico, los datos del objeto se aplicarán al gráfico desde la plantilla, y al iniciar este asesor y/o indicador, aquellos crearán de nuevo estos mismos objetos.

La consecuencia de una acción de este tipo puede ser la creación de un número descontrolado de objetos idénticos en el gráfico, lo que provocará la saturación del gráfico y un consumo excesivo e innecesario de recursos informáticos. Pero también es posible la peor variante, cuando el programa necesario genera un error al crear un objeto y se descarga del gráfico, lo que provocará su desactivación.

Los objetos creados mediante programación están protegidos contra la modificación por parte del usuario, para ello, se los oculta en la lista de objetos gráficos asignando a un objeto la propiedad de oculto. Para corregir dicha propiedad, la plantilla ofrece el indicador hidden, que tiene asignado un valor 1 para los objetos ocultos.

Por lo tanto, para habilitar/deshabilitar un asesor experto, basta con reescribir el archivo de plantilla, cambiando en él la bandera expertmode al valor deseado y eliminando al mismo tiempo los objetos ocultos. El uso de la nueva plantilla reiniciará el asesor con la propiedad que necesitemos.

3. Definiendo los comandos

Tras definir los nuevos principios de funcionamiento del asesor desarrollado, ha llegado el momento de desarrollar un sistema de mensajería. Ya hemos decidido utilizar órdenes para enviar comandos. ¿Pero cómo enviar comandos con órdenes, sin estropear el resultado financiero? Para estos fines, utilizaremos las órdenes pendientes, que serán eliminados por el asesor después de recibir el comando.

Al colocar órdenes pendientes, debemos estar seguros de que estas no funcionarán en el intervalo temporal de la orden pendiente. La propia solución a esta cuestión es obvia: debemos colocar la orden a una distancia suficiente del precio actual.

Para nuestra sorpresa, al trabajar con el terminal móvil, hemos detectado la posibilidad de dejar comentarios a las órdenes abiertas. Al mismo tiempo, el usuario puede ver los comentarios a las órdenes creadas en el terminal de escritorio. De esta forma, podemos organizar una comunicación unidireccional para la transferencia de mensajes del asesor-maestro al usuario. Pero el usuario no podrá dar órdenes de esta forma.

Por lo tanto, podemos colocar órdenes pendientes que serán leídas por el asesor, pero no podemos dejar comentarios en ellas. En este caso, podemos usar el precio y el instrumento de la orden para transferir el comando. Y aquí debemos determinar qué precio establecer para codificar el comando, ya que debe estar a una distancia suficiente del precio actual para que la orden colocada no se active bajo ninguna circunstancia.

A nuestro juicio, tales precios pueden ser aquellos que se encuentran próximos a cero. Teóricamente, la probabilidad de que el precio de cualquier instrumento se aproxime a cero es ínfima. Por lo tanto, al establecer el precio del pedido en 1-5 ticks, prácticamente no nos arriesgamos a que se active. Naturalmente, en tal caso, tendremos que limitar nuestro stop con órdenes de venta, ya que el uso de órdenes stop no influye en el margen libre.

No olvidemos que en la aplicación móvil podemos leer los comentarios de la orden, es decir, nuestro asesor-maestro nos puede enviar información de esta forma.

Resumiendo lo dicho, proponemos la siguiente codificación de comandos:

Precio Volumen Comando
1 1 tick cualquiera Solicitar el estado de los asesores.
El asesor-maestro coloca órdenes pendientes de los instrumentos con los asesores, en los comentarios a la orden se indica la denominación del asesor y el permiso de comerciar.
2 2 ticks cualquiera El asesor detiene el comercio.
Si la orden no tiene comentarios, se detendrán todos los asesores. Si el comando se ha dado como resultado de la modificación de una orden después del primer comando, se detendrá un asesor concreto en un instrumento concreto.
3 3 ticks cualquiera El asesor inicia el comercio.
Los principios de funcionamiento son análogos al comando 2.
4 4 ticks 1 min.lote BUY
2 min. lote SELL
3 min. lote TODOS
Elimina las órdenes pendientes.
5 5 ticks 1 min.lote BUY
2 min. lote SELL
3 min. lote TODOS
Cierra las posiciones.


4. Creando el experto-maestro

Ahora que comprendemos completamente los métodos de trabajo y el canal de conexión, podemos comenzar a escribir nuestro asesor. En el código de nuestro asesor se usarán los métodos de la biblioteca "Expert - bibilioteca para MetaTrader 5" con un pequeño cambio: para mayor comodidad de uso, haremos públicos todos los métodos de la biblioteca.

Al principio, para que nos resulte más cómodo, asignaremos nombres mnemotécnicos a los tags utilizados. Parte de los mismos ya ha sido declarada en la biblioteca usada.

#define FILENAME (__FILE__ + ".tpl")
#define PATH "\\Files\\"
#define STRING_END "\r\n"
#define EXPERT_BEGIN ("<expert>" + STRING_END)
#define EXPERT_END ("</expert>" + STRING_END)
#define EXPERT_INPUT_BEGIN ("<inputs>" + STRING_END)
#define EXPERT_INPUT_END ("</inputs>" + STRING_END)
#define EXPERT_CHART_BEGIN ("<chart>" + STRING_END)
#define EXPERT_NAME "name="
#define EXPERT_PATH "path="
#define EXPERT_STOPLEVEL "stops_color="

Los adicionales los declararemos en el código de nuestro asesor.

#define OBJECT_BEGIN             ("<object>" + STRING_END)
#define OBJECT_END               ("</object>" + STRING_END)
#define OBJECTS_NUMBER           ("objects=")
#define OBJECT_HIDDEN            ("hidden=1")
#define EXPERT_EXPERTMODE        ("expertmode=")

También debemos prestar atención a que fxsaber se ha preocupado de la compatibilidad de su biblioteca con otro código y ha cancelado los nombres al final de la biblioteca. Este enfoque excluye la aparición de errores de asignación repetida de un nombre análogo a otra macros, pero no da la posibilidad de usar estas declaraciones fuera de la biblioteca. Para no realizar declaraciones análogas de macros de forma repetida en el código del asesor, cerraremos con comentarios las directivas #undef en el código de la biblioteca.

//#undef EXPERT_STOPLEVEL
//#undef EXPERT_PATH
//#undef EXPERT_NAME
//#undef EXPERT_CHART_BEGIN
//#undef EXPERT_INPUT_END
//#undef EXPERT_INPUT_BEGIN
//#undef EXPERT_END
//#undef EXPERT_BEGIN
//#undef STRING_END
//#undef PATH
//#undef FILENAME

A continuación, declararemos las dos variables externas de nuestro asesor-maestro: el tiempo de vida útil de las órdenes en minutos y el número mágico para su identificación.

sinput int      Expirations =  5;
sinput ulong    Magic       =  88888;

Vamos a añadir al código de nuestro asesor la biblioteca indicada más arriba y la biblioteca de operaciones comerciales.

#include <fxsaber\Expert.mqh>
#include <Trade\Trade.mqh>

En el bloque de variables globales, declaramos un ejemplar de clase de las operaciones comerciales y las variables para guardar el identificador del gráfico de trabajo y el ticket de la última orden de comando.

CTrade   *Trade;
ulong     chart;
ulong     last_command;

En la función OnInit inicializamos las variables globales.

int OnInit()
  {
//---
   Trade =  new CTrade();
   if(CheckPointer(Trade)==POINTER_INVALID)
      return INIT_FAILED;
   Trade.SetDeviationInPoints(0);
   Trade.SetExpertMagicNumber(Magic);
   Trade.SetMarginMode();
   Trade.SetTypeFillingBySymbol(_Symbol);
//---
   chart=ChartID();
   last_command=0;
//---
   return(INIT_SUCCEEDED);
  }

Y no olvidemos eliminar el ejemplar de clase de las operaciones comerciales en la función OnDeinit.

void OnDeinit(const int reason)
  {
//---
   if(CheckPointer(Trade)!=POINTER_INVALID)
      delete Trade;
  }


4.1. Gestor de comandos.

El inicio de la funcionalidad principal del asesor-maestro se realizará a partir de la función OnTradeTransaction. Debemos recordar que el evento de llamada de esta función puede procesarse varias veces para una orden. Por eso, antes de iniciar el código del programa, vamos a realizar una serie de comprobaciones.

Vamos a comprobar la presencia del ticket de la orden para el evento procesado. Vamos a comprobar el estado de la orden, para no procesar el evento de eliminación de órdenes. Comprobaremos también el número mágico de la orden, para excluir el procesamiento de los asesores. Asimismo, vamos a verificar el tipo de orden, puesto que nuestros comandos llegan solo con órdenes stop de venta. Además, comprobaremos el tipo de operación comercial: este deberá ser o bien la adición de una orden o bien su modificación.

Para superar con éxito todas las comprobaciones, iniciaremos el gestor de comandos, que se organiza en la función CheckCommand.

void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//---
   if(trans.order>0 && trans.order_state!=ORDER_STATE_REQUEST_CANCEL && request.magic==0 && 
      trans.order_type==ORDER_TYPE_SELL_STOP && 
      (trans.type==TRADE_TRANSACTION_ORDER_ADD || trans.type==TRADE_TRANSACTION_ORDER_UPDATE))
      CheckCommand(trans,request);
  }

Al iniciar la función CheckCommand, esta recibe en sus parámetros la estructura de la operación y la solicitud comercial. En primer lugar, comprobamos si no se ha procesado anteriormente la solicitud actual. Si el comando ya ha sido procesado, salimos de la función.

void CheckCommand(const MqlTradeTransaction &trans,
                  const MqlTradeRequest &request)
  {
   if(last_command==trans.order)
      return;

Si el comando aún no ha sido procesado, decodificamos el precio de la orden en el comando.

   double tick=SymbolInfoDouble(trans.symbol,SYMBOL_TRADE_TICK_SIZE);
   uint command=(uint)NormalizeDouble(trans.price/tick,0);

A continuación, con la ayuda del operador switch, llamamos la función correspondiente al comando entrante.

   switch(command)
     {
      case 1:
        if(StringLen(request.comment)>0 || trans.type==TRADE_TRANSACTION_ORDER_UPDATE)
           return;
        if(trans.order<=0 || (OrderSelect(trans.order) && StringLen(OrderGetString(ORDER_COMMENT))>0))
           return;
        GetExpertsInfo();
        break;
      case 2:
        ChangeExpertsMode(trans,request,false);
        break;
      case 3:
        ChangeExpertsMode(trans,request,true);
        break;
      case 4:
        DeleteOrders(trans);
        break;
      case 5:
        ClosePosition(trans);
        break;
      default:
        return;
     }

Finalmente, guardamos el ticket del último comando de la orden y eliminamos la orden de la cuenta.

   last_command=trans.order;
   Trade.OrderDelete(last_command);
  }

Podrá familiarizarse con el código completo de todas las funciones en los anexos.


4.2. Información sobre los asesores iniciados.

La función GetExpertsInfo se encarga de recibir la información sobre los asesores iniciados en el terminal. Como ya hemos decidido antes, esta información colocorá las órdenes stop informativas, cuyo instrumento representa en qué instrumento se ha iniciado el asesor, mientras que en los comentarios a la orden se representarán la denominación del asesor y su estado.

Al inicio de la función, eliminamos las órdenes colocadas, llamando la función DeleteOrdersByMagic.

void GetExpertsInfo(void)
  {
   DeleteOrdersByMagic(Magic);

Después organizamos un ciclo de iteración por todos los gráficos activos en el terminal. Comenzaremos el ciclo comprobando si el gráfico analizado es un gráfico de trabajo de nuestro asesor-maestro. Si es así, pasamos al siguiente gráfico.

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

En la siguiente etapa, cargaremos la plantilla del gráfico con la comprobación preliminar sobre la presencia del experto en el gráfico. Si el experto no se encuentra en el gráfico, o la plantilla no ha sido cargada, pasamos al gráfico siguiente.

      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

A continuación, encontramos en la plantilla el bloque del experto. Si no hemos encontrado el bloque, pasamos al gráfico siguiente.

      temp=EXPERT::StringBetween(temp,EXPERT_BEGIN,EXPERT_END);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Después, extraemos la denominación del experto y su estado. A partir del mismo, se forma el comentario de nuestra orden informativa. Si el asesor tiene permitido comerciar, antes de su denominación, en los comentarios, pondremos la letra "T", de lo contrario, la letra "S".

      string name =  EXPERT::StringBetween(temp,EXPERT_NAME,STRING_END);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,EXPERT_EXPERTMODE,STRING_END));
      string comment =  (state ? "T " : "S ")+name;

Como final del ciclo, determinaremos el instrumento del gráfico, el plazo de duración de la orden informativa, enviaremos la orden y pasaremos al siguiente gráfico.

      string symbol=ChartSymbol(i_chart);
      ENUM_ORDER_TYPE_TIME type=ORDER_TIME_GTC;
      datetime expir=0;
      if(Expirations>0)
        {
         expir=TimeCurrent()+Expirations*60;
         type=ORDER_TIME_SPECIFIED;
        }
      Trade.SellStop(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN),SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE),symbol,0,0,type,expir,comment);
      i_chart=ChartNext(i_chart);
     }
  }

En la función analizada se han usado los métodos de la biblioteca de fxsaber: la obtención de la plantilla del gráfico en una variable de cadena y la obtención de una subcadena entre los tags indicados.

Para obtener la plantilla en una variable de cadena según el gráfico indicado, primero, en caso necesario, comprobamos la presencia del experto en el gráfico. A continuación, guardamos la plantilla del gráfico indicado y calculamos la plantilla obtenida como una matriz binaria. Después, transformamos la matriz numérica en una cadena y retornamos la función llamada. Si hubiera errores en cualquiera de los estadios de comprobación, la función retornará el valor NULL.

  static string TemplateToString( const long Chart_ID = 0, const bool CheckExpert = false )
  {
    short Data[];
    return(((!CheckExpert || EXPERT::Is(Chart_ID)) && ::ChartSaveTemplate((ulong)Chart_ID, PATH + FILENAME) && (::FileLoad(FILENAME, Data) > 0)) ?
           ::ShortArrayToString(Data) : NULL);
  }

Para obtener la subcadena entre los tags indicados, primero determinamos la posición del inicio y el final de la subcadena.

  static string StringBetween( string &Str, const string StrBegin, const string StrEnd = NULL )
  {
    int PosBegin = ::StringFind(Str, StrBegin);
    PosBegin = (PosBegin >= 0) ? PosBegin + ::StringLen(StrBegin) : 0;

    const int PosEnd = ::StringFind(Str, StrEnd, PosBegin);

A continuación, cortamos la subcadena para el retorno y reducimos la cadena original hasta el resto no procesado.

    const string Res = ::StringSubstr(Str, PosBegin, (PosEnd >= 0) ? PosEnd - PosBegin : -1);
    Str = (PosEnd >= 0) ? ::StringSubstr(Str, PosEnd + ::StringLen(StrEnd)) : NULL;

    if (Str == "")
      Str = NULL;

    return(Res);
  }

Podrá familiarizarse con el código completo de todas las funciones y clases en los anexos.

4.3. Función de cambio de estado del experto.

El cambio del estado de los expertos se realiza en la función ChangeExpertsMode. En sus parámetros, la función indicada recibe la estructura de la operación comercial y la solicitud comercial, así como el nuevo estado para instalar el asesor.

void ChangeExpertsMode(const MqlTradeTransaction &trans,
                       const MqlTradeRequest &request,
                       bool  ExpertMode)
  {
   string comment=request.comment;
   if(StringLen(comment)<=0 && OrderSelect(trans.order))
      comment=OrderGetString(ORDER_COMMENT);      
   string exp_name=(StringLen(comment)>2 ? StringSubstr(comment,2) : NULL);

A continuación, se organiza un ciclo de iteración de todos los gráficos con carga de las plantillas, como se ha descrito más arriba.

   long i_chart=ChartFirst();
   while(i_chart>=0 && !IsStopped())
     {
      if(i_chart==0 || i_chart==chart || (StringLen(exp_name)>0 && ChartSymbol()!=trans.symbol))
        {
         i_chart=ChartNext(i_chart);
         continue;
        }
      string temp=EXPERT::TemplateToString(i_chart,true);
      if(temp==NULL)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

En la siguiente etapa, en caso necesario, comprobamos la presencia del experto necesario en el gráfico. Si el asesor iniciado en el gráfico no se corresponde con la solicitud, pasamos al siguiente gráfico. Asimismo, comprobamos el estado del asesor, en el caso de que se corresponda con el indicado para la instalación, pasamos al gráfico siguiente.

      string NewTemplate   =  NULL;
      if(exp_name!=NULL)
        {
         NewTemplate=EXPERT::StringBetween2(temp,NULL,EXPERT_NAME);
         string name=EXPERT::StringBetween(temp,NULL,STRING_END);
         if(name!=exp_name)
           {
            i_chart=ChartNext(i_chart);
            continue;
           }
         NewTemplate+=name+STRING_END;
        }
//---
      NewTemplate+=EXPERT::StringBetween2(temp,NULL,EXPERT_EXPERTMODE);
      bool state  =  (bool)StringToInteger(EXPERT::StringBetween(temp,NULL,STRING_END));
      if(state==ExpertMode)
        {
         i_chart=ChartNext(i_chart);
         continue;
        }

Tras superar todas las comprobaciones necesarias, creamos una nueva plantilla en la que se indique el estado del experto. Eliminamos de la plantilla los objetos ocultos y aplicamos la nueva plantilla al gráfico. Después de ejecutar todas las operaciones, pasamos al gráfico siguiente.

      NewTemplate+=IntegerToString(ExpertMode)+STRING_END+temp;
      NewTemplate=DeleteHiddenObjects(NewTemplate);
      EXPERT::TemplateApply(i_chart,NewTemplate,true);
//---
      i_chart=ChartNext(i_chart);
     }

Una vez finalizado el ciclo de iteración, iniciamos la función de recopilación de información sobre los aserores iniciados para mostrar al usuario el nuevo estado de los asesores.

   GetExpertsInfo();
  }

Podrá familiarizarse con el código completo de todas las funciones en los anexos.

4.4. Función de eliminación de órdenes y cierre de posiciones

Las funciones de eliminación de órdenes y cierre de posiciones abiertas se han construido según un mismo algoritmo y solo se distinguen por sus objetos de influencia. Por eso, en el artículo proponemos analizar solo uno de llos. Al llamar la función DeleteOrders en los parámetros, a esta se le transmite la estructura de la operación comercial a partir de la cual el volumen de la orden se decodifica en dirección de las órdenes cerradas (de acuerdo con el recuadro de comandos del apartado 3).

void DeleteOrders(const MqlTradeTransaction &trans)
  {
   int direct=(int)NormalizeDouble(trans.volume/SymbolInfoDouble(trans.symbol,SYMBOL_VOLUME_MIN),0);

A continuación, se organiza el ciclo de iteración por todas las órdenes colocadas en la cuenta. En este ciclo se comprueba si el tipo de orden se corresponde con el comando entrante. Si coinciden, se envía una solicitud de eliminación de la orden.

   for(int i=total-1;i>=0;i--)
     {
      ulong ticket=OrderGetTicket((uint)i);
      if(ticket<=0)
         continue;
//---
      switch((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE))
        {
         case ORDER_TYPE_BUY_LIMIT:
         case ORDER_TYPE_BUY_STOP:
         case ORDER_TYPE_BUY_STOP_LIMIT:
           if(direct==2)
              continue;
           Trade.OrderDelete(ticket);
           break;
         case ORDER_TYPE_SELL_LIMIT:
         case ORDER_TYPE_SELL_STOP:
         case ORDER_TYPE_SELL_STOP_LIMIT:
           if(direct==1)
              continue;
           Trade.OrderDelete(ticket);
           break;
        }
     }
  }

Podrá familiarizarse con el código completo de todas las funciones en los anexos.


Conclusión

En el presente artículo se ofrece un método para el control remoto del funcionamiento de los asesores en el terminal MetaTrader 5. Esta solución permite aumentar la movilidad de los tráders que usen robots comerciales en su trabajo. El enfoque aplicado al uso de las funciones estándar, bastante heterodoxo, permite resolver tareas más amplias sin usar diferentes dll.

Enlaces

Expert - biblioteca para MetaTrader 5

Programas usados en el artículo:

#
Nombre
Tipo
Descripción
1 Expert.mqh Biblioteca de clase Expert - biblioteca para MetaTrader 5
2 Master.mq5 Asesor Asesor-maestro para controlar otros asesores iniciados en el terminal
3 Master.mqproj Archivo del proyecto Proyecto del asesor-maestro


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

Archivos adjuntos |
MQL5.zip (88.19 KB)
Implementación de Take Profit en forma de órdenes limitadas sin cambiar el código fuente del EA Implementación de Take Profit en forma de órdenes limitadas sin cambiar el código fuente del EA

Desde hace mucho en el foro se discute la cuestión del uso de órdenes limitadas en vez de colocar el Take Profit estándar para la posición. ¿En qué consiste la ventaja de este enfoque y cómo se puede implementarlo en la negociación? En este artículo me gustaría proponerles mi propia visión de las respuestas a estas preguntas.

Reversión: ¿es el Santo Grial o una peligrosa equivocación? Reversión: ¿es el Santo Grial o una peligrosa equivocación?

En el presente artículo intentaremos aclarar lo siguiente: ¿qué es una reversión, si merece la pena usarla y si podemos mejorar nuestra estrategia comercial a través de ella? Vamos a crear un Asesor Experto, y veremos en los datos históricos qué indicadores convienen mejor para la reversión, además, si podemos usarla sin indicadores como un sistema comercial independiente. Veremos si es posible convertir un sistema comercial no rentable en un sistema rentable a través de la reversión.

Optimización automática de EAs en MetaTrader 5 Optimización automática de EAs en MetaTrader 5

En este artículo se describe el mecanismo de auto-optimización de un experto que funcione en MetaTrader 5.

Gap - ¿una estrategia rentable o 50/50? Gap - ¿una estrategia rentable o 50/50?

La investigación de la aparición de gaps se relaciona con la situación en la que se da una diferencia sustancial entre el precio de cierre del marco temporal anterior y el precio de apertura del siguiente, así como en la dirección en la que irá la barra diaria. Uso de la función DLL GetOpenFileName de sistema.