Discusión sobre el artículo "Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXV): Procesando los errores retornados por el servidor comercial"

 

Artículo publicado Biblioteca para el desarrollo rápido y sencillo de programas para MetaTrader (Parte XXV): Procesando los errores retornados por el servidor comercial:

Después de enviar una orden comercial al servidor, no deberíamos pensar que "ya está todo hecho", ya que ahora tendremos que comprobar los códigos de error, o bien la ausencia de los mismos. En el presente artículo, vamos a implementar el procesamiento de los errores retornados por el servidor comercial, preparando asimismo la base para crear solicitudes comerciales pendientes.

En las últimas versiones de MetaTrader 5, a partir del build 2201, en el simulador ha aparecido la posibilidad de establecer los parámetros del símbolo en el que se realiza la simulación. De esta forma, es posible establecer limitaciones para el comercio con el símbolo y simular el comportamiento de la biblioteca al detectar las limitaciones indicadas para el símbolo.

Para llamar a la ventana de ajustes del símbolo, debemos clicar sobre el botón que se encuentra a la derecha de la selección del marco temporal a simular:

Establecemos para el símbolo el permiso de comercio solo para posiciones largas, e indicamos una limitación del volumen para las posiciones simultáneamente abiertas y colocadas en una misma dirección igual a 0.5.

De esta forma, podremos comerciar solo con posiciones largas, y tener en el mercado un volumen conjunto máximo de posiciones y órdenes de compra no superior a 0.5 lotes. Es decir, si abrimos posiciones con un lote de 0.1, podremos abrir solo cinco posiciones, o colocar una orden pendiente de compra y abrir cuatro posiciones:


Aquí, para que el experimento resulte más puro, deberíamos desactivar el cierre automático de posiciones al superar el beneficio con la magnitud establecida en los ajustes. No obstante, así también se ve que no hemos podido abrir una posición corta al principio: hemos recibido la advertencia de que en el símbolo solo se permite la compra. A continuación, al intentar abrir un número de posiciones cuyo vulumen conjunto supere los 0.5 lotes, recibimos un mensaje indicando la imposibilidad de abrir una posición, dado que se superará el volumen conjunto máximo de posiciones y órdenes en una misma dirección.

Autor: Artyom Trishkin

 
Hola! he descargado la última versión de la biblioteca y Expert Part_23, puse el modo visual en el tester y se abren las órdenes de mercado, pero todas las órdenes pendientes no.
las ordenes se abren, pero todas las ordenes pendientes no, en el diario se escribe "2019.10.27 10:13:32.157 2019.09.23 10:00:02 failed sell stop limit 2.00 RTS-12.19 at 135750 (135800) sl: 135900 tp: 135600 [Invalid expiration].

Símbolos de cambio, broker Otkritie, versión 5.00 build 2190.

Perdón, había una continuación de la discusión en la parte 23, pero me he liado y me la he perdido. De todos modos, he descargado la versión 25 y el problema persiste. En la discusión de la parte 23 escribiste que es necesario insertar líneas añadidas en onInit'e después de ciertas líneas, pero estas líneas resaltadas por ti en verde no las tengo en onInit'e ni en la versión 23 ni en la 25, aquí está todo el onInit :

//--- Estableciendo las variables globales de EA

prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";

for(int i=0;i<TOTAL_BUTT;i++)

{

butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);

butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);

}

lot=NormaliseLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));

magic_number=InpMagic;

stoploss=InpStopLoss;

takeprofit=InpTakeProfit;

distancia_pendiente=InpDistancia;

distancia_stoplimit=InpDistanciaSL;

deslizamiento=InpSlippage;

trailing_stop=InpTrailingStop*Punto();

trailing_step=InpTrailingStep*Punto();

trailing_start=InpTrailingStart;

stoploss_to_modify=InpStopLossModify;

takeprofit_to_modify=InpTakeProfitModify;


//--- Inicialización de la librería DoEasy

OnInitDoEasy();

//--- Comprobación y borrado de objetos gráficos no liberados del Asesor Experto

if(IsPresentObects(prefix))

ObjectsDeleteAll(0,prefix);


//--- Crear la barra de botones

if(!CreateButtons(InpButtShiftX,InpButtShiftY))

return INIT_FAILED;

//--- Establecer el estado del botón de activación posterior

ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);

//tr.SetCorrectTypeExpiration();

engine.TradingSetCorrectTypeExpiration();

engine.TradingSetCorrectTypeFilling();

//--- Comprobación de la reproducción del sonido estándar por sustitución de macro y del sonido personalizado por descripción

engine.PlaySoundByDescription(SND_OK);

Sleep(600);

engine.PlaySoundByDescription(TextByLanguage("El sonido de una moneda que cae 2", "El sonido de una moneda que cae 2"));


//---

return(INIT_SUCCEED);

 
Alexander:
Hola, he descargado la última versión de la biblioteca y Experto Part_23, poner el modo visual y el modo de mercado en el probador
las ordenes se abren, pero todas las ordenes pendientes no, en el diario escribe "2019.10.27 10:13:32.157 2019.09.23 10:00:02 failed sell stop limit 2.00 RTS-12.19 at 135750 (135800) sl: 135900 tp: 135600 [Invalid expiration].

Símbolos de cambio, broker Otkritie, versión 5.00 build 2190.

Perdón, había una continuación de la discusión en la parte 23, pero me he liado y me la he perdido. De todos modos, he descargado la versión 25 y el problema persiste. En la discusión de la parte 23 escribiste que es necesario insertar líneas añadidas en onInit'e después de ciertas líneas, pero estas líneas resaltadas por ti en verde no las tengo en onInit'e en absoluto ni en la versión 23 ni en la 25, aquí está todo el onInit:

¿Estás usando el EA de prueba del artículo 25 - "TestDoEasyPart25.mq5" ?

 
Ahora he insertado onInit de la versión 23 con las funciones añadidas que he insertado, pero he usado la versión 25 que tampoco tiene las líneas que has resaltado en verde en la versión 23 y la versión 25 tampoco funciona como la 23.
 
Alexander:
Ahora he insertado onInit de la versión 23 con las funciones añadidas que he insertado, pero he usado la versión 25 que tampoco tiene las líneas que has resaltado en verde en la versión 23 y la versión 25 no funciona tan bien como la 23.

No entiendo a qué líneas resaltadas en verde te refieres.

Y por favor, no utilice un EA de prueba de otro artículo - cada artículo viene con su propio EA para pruebas - debe utilizarlo e informar de cualquier error que encuentre en relación con el EA de prueba correcto, no de otro artículo.

 

Ok, lo siento, en relación con el último artículo:

Hola, he descargado la última versión de la librería y del Expert Part_25, he puesto el modo visual en el tester y las órdenes de mercado se abren, pero las pendientes no.

órdenes se abren, pero todas las órdenes pendientes no son, el registro dice 2019.11.21 12:59:20.689 2019.11.18 10:00:02 fallido vender límite de parada 2.00 Si-12.19 en 63972 (64022) sl: 64072 tp: 63972 [Vencimiento no válido] y 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Intento de operación #3. Error : Fecha de caducidad de la orden no válida en la solicitud.

Símbolos de bolsa, broker Otkritie, versión 5.00 build 2190.
 
Alexander:

Vale, perdón, en relación con el último artículo:

Hola! Descargado la última versión de la biblioteca y Experto Part_25, poner el modo visual y el modo de mercado en el probador

órdenes se abren, pero todas las órdenes pendientes no son, el registro dice 2019.11.21 12:59:20.689 2019.11.18 10:00:02 falló vender límite de parada 2.00 Si-12.19 en 63972 (64022) sl: 64072 tp: 63972 [Vencimiento no válido] y 2019.11.21 12:59:20.689 2019.11.18 10:00:02 Intento de operación #3. Error : Fecha de caducidad de la orden no válida en la solicitud.

Símbolos de bolsa, broker Otkritie, versión 5.00 build 2190.

Ok, gracias, lo miraré.

¿Puedes adjuntar aquí una captura de pantalla con la especificación del símbolo donde te da el error?

 
En el expediente figura una captura de pantalla del pliego de condiciones Si-12.19.
Archivos adjuntos:
 
Alexander:
Pantalla de la especificación Si-12.19 en el archivo.

En esta versión de la librería, así como en la siguiente, se me pasó comprobar los tipos de llenado de órdenes y los tipos de vencimiento. Habrá correcciones en el próximo (27) artículo.

Por ahora, puedo sugerir que al enviar una solicitud de operación, debe especificar explícitamente el tipo de vencimiento. Por ejemplo, para colocar una orden pendiente de Límite de Venta, debe agregarlo en el Asesor Experto de prueba:

//--- Si se pulsa el botón BUTT_SELL_LIMIT: Fijar SellLimit
else if(button==EnumToString(BUTT_SELL_LIMIT))
  {
   //--- Establecer orden SellLimit
   engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("PendienteVenderLímite","Pending order SellLimit"),0,ORDER_TIME_DAY);
  }

Y hacer lo mismo en todas las líneas que contienen la llamada de métodos para establecer órdenes pendientes.

 

Artem, gracias por el trabajo que has hecho, muy reflexivo.

Para ser honesto, yo no entendía muy bien el punto de números mágicos compuestos....

Supongamos que voy a dividir las operaciones en un EA en varios grupos con diferentes números mágicos.

En el enfoque clásico, posteriormente recorro las órdenes y comparo los números mágicos, distribuyendo el resultado entre los grupos.

En la biblioteca, entiendo que no cambia mucho en esta situación - sólo la asignación de estos números se puede sistematizar de alguna manera? ¿O hay algo más que afecten?


Y luego hay una capa particular de la funcionalidad que me falta categóricamente en su biblioteca: la agregación.

Muy a menudo tenemos que responder a la pregunta, ¿cuál es el beneficio agregado en un conjunto de parámetros (magik, símbolo, tipo de orden). Y el lote agregado, por ejemplo. Y el nivel de equilibrio. Y el número de operaciones.

Estos valores agregados se necesitan en todas partes y repetidamente, calcularlos por muestreo en cada nuevo método es un exceso obvio.

Sería genial tener un conjunto de agregaciones, configurando cada una con un conjunto de criterios de muestreo (símbolo, tipo, magik, número de grupo) y proporcionar acceso a ellos en CEngine - y actualizarlos una vez en Refresh.

Si usted no tiene planes para este tipo de refinamiento, podría finalizar y compartir..... pero sólo tenemos significativamente diferente estilo de código, esta pieza se vería extraño.

 
rigal:

Artem, gracias por el trabajo que has hecho, muy considerado.

No entendí muy bien lo de los magos compuestos, la verdad....

Supongamos que voy a dividir las operaciones en un Asesor Experto en varios grupos con diferentes números mágicos.

En el enfoque clásico, posteriormente voy a través de las órdenes y comparar los números mágicos, distribuyendo el resultado entre los grupos.

En la biblioteca, entiendo que no cambia mucho en esta situación - sólo la asignación de estos números se puede sistematizar de alguna manera? ¿O hay algo más que afecten?


Y también hay una cierta capa de funcionalidad que me falta categóricamente en su biblioteca: la agregación.

Muy a menudo necesitamos responder a la pregunta, cuál es el beneficio agregado sobre un conjunto de parámetros (magik, símbolo, tipo de orden). Y el lote agregado, por ejemplo. Y el nivel de equilibrio. Y el número de operaciones.

Estos valores agregados se necesitan en todas partes y repetidamente, calcularlos por muestreo en cada nuevo método es una obvia exageración.

Sería genial tener un conjunto de agregaciones, configurando cada una con un conjunto de criterios de muestreo (símbolo, tipo, magik, número de grupo) y proporcionar acceso a ellos en CEngine - y actualizarlos una vez en Refresh.

Si usted no tiene planes para este tipo de refinamiento, podría finalizar y compartir..... pero sólo tenemos significativamente diferente estilo de código, esta pieza se vería extraño.

Acerca de la información registrada en el valor Magic Number:

puede utilizar un magik diferente para cada grupo para crear diferentes grupos. Por ejemplo, si el magik del asesor es 123, el magik del primer grupo será 124, el del segundo 125, el del tercero 126, etc.
La biblioteca ofrece una forma diferente de crear diferentes grupos - el número de cada subgrupo se almacena directamente en el valor del Número Mágico. Entonces el número mágico del EA es también un identificador de grupo, pero se coloca en un grupo independiente llamado MagicID - el identificador del número mágico del EA. Y hay dos grupos más. Cada uno de ellos tiene 15 subgrupos. Y cada uno de los subgrupos puede tener su propio identificador.

Esto darámás flexibilidad a la hora de trabajar con grupos.

Por ejemplo: Queremos mover una rejilla de órdenes pendientes detrás del precio - las añadimos al grupo 1 en el subgrupo 1. El grupo 1 se mueve detrás del precio. El subgrupo 1 se mueve a lo largo de la MA. Ahora queremos mover algunas de esas órdenes que se mueven detrás del precio (grupo 1) por el SAR Parabólico. Les damos el subgrupo 2. Entonces el grupo 1 se mueve detrás del precio, pero el subgrupo 1 se mueve por MA, y el subgrupo 2 se mueve por SAR Parabólico.

Las órdenes se activan, convirtiéndose en posiciones - usted puede establecer sus propios grupos para la modificación de StopLoss, y sus propios subgrupos en este grupo para la modificación por diferentes valores. Los algoritmos de modificación se escriben en subgrupos.

En general - un vuelo de la fantasía. También puede utilizar un simple magik, pero usted tendrá que pensar por sí mismo la lógica de seguimiento de los diferentes grupos.

En la segunda pregunta:

Hay una clase CSelect. Está disponible en el programa, y proporciona métodos de selección y búsqueda, que usted escribe, de todas las colecciones existentes: Account, Event, Order, Symbol.

Puede seleccionar los objetos de cada colección en una lista basada en todos los criterios. En la lista creada, puede volver a seleccionar refinando los criterios, puede encontrar los valores máximos y mínimos para el criterio de selección.

Sin embargo, habrá más funciones de usuario (mucho más adelante) para un acceso rápido y cómodo a todas las propiedades de todas las colecciones y buscar en ellas.

Pero por ahora - sólo a través de CSelect, y cuando lo necesite. La clase es estática, por lo que el acceso a sus métodos a través de ":::" Por ejemplo, CSelect::ByOrderProperty().

Sí, por cierto, hay un ejemplo de su uso en el programa justo en la prueba EA - por ejemplo, en sus funciones de arrastre:

//+------------------------------------------------------------------+
//| Busca las órdenes pendientes más lejanas.
//+------------------------------------------------------------------+
void TrailingOrders(void)
  {
   MqlTick tick;
   if(!SymbolInfoTick(Symbol(),tick))
      return;
   double stop_level=StopLevel(Symbol(),2)*Point();
//--- Obtener la lista de todos los pedidos establecidos
   CArrayObj* list=engine.GetListMarketPendings();
   //--- Seleccionar sólo órdenes por el símbolo actual de la lista
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
//--- Seleccione de la lista sólo las órdenes en la dirección de Compra
   CArrayObj* list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_BUY,EQUAL);
   //--- Ordena la lista por distancia del precio en pips (por beneficio en pips)
   list_buy.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Obtener el índice de la orden en la dirección de Compra con la mayor distancia
   int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_PT);
   if(index_buy>WRONG_VALUE)
     {
      COrder* buy=list_buy.At(index_buy);
      if(buy!=NULL)
        {
         //--- Si la orden está fijada por debajo del precio (BuyLimit) y necesita ser "levantada" por detrás del precio
         if(buy.TypeOrder()==ORDER_TYPE_BUY_LIMIT)
           {
            //--- Calcular el nuevo precio de ajuste de la orden y los niveles de stop a partir del nuevo precio
            double price=NormalizeDouble(tick.ask-trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Si el precio calculado es inferior a la distancia StopLevel, se pospone desde el precio de establecimiento de la orden Ask (se observa la distancia StopLevel)
            if(price<tick.ask-stop_level) 
              {
               //--- Si el precio calculado es superior al paso de arrastre aplazado del precio de fijación de la orden - modifique el precio de fijación de la orden
               if(price>buy.PriceOpen()+trailing_step)
                 {
                  engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1);
                 }
              }
           }
         //--- Si la orden está fijada por encima del precio (BuyStop y BuyStopLimit) y debe ser "bajada" por detrás del precio
         else
           {
            //--- Calcular el nuevo precio de ajuste de la orden y los niveles de stop a partir del nuevo precio
            double price=NormalizeDouble(tick.ask+trailing_stop,Digits());
            double sl=(buy.StopLoss()>0 ? NormalizeDouble(price-(buy.PriceOpen()-buy.StopLoss()),Digits()) : 0);
            double tp=(buy.TakeProfit()>0 ? NormalizeDouble(price+(buy.TakeProfit()-buy.PriceOpen()),Digits()) : 0);
            //--- Si el precio calculado es superior a la distancia StopLevel, se pospone desde el precio de fijación de la orden Ask (se observa la distancia StopLevel)
            if(price>tick.ask+stop_level) 
              {
               //--- Si el precio calculado es inferior al paso de arrastre aplazado del precio de fijación de la orden - modifique el precio de fijación de la orden
               if(price<buy.PriceOpen()-trailing_step)
                 {
                  engine.ModifyOrder((ulong)buy.Ticket(),price,sl,tp,-1);
                 }
              }
           }
        }
     }
//--- Seleccionar de la lista sólo las órdenes en dirección Venta
   CArrayObj* list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_DIRECTION,ORDER_TYPE_SELL,EQUAL);
   //--- Ordena la lista por distancia del precio en pips (por beneficio en pips)
   list_sell.Sort(SORT_BY_ORDER_PROFIT_PT);
   //--- Obtener el índice de la orden en la dirección Sell con la mayor distancia
   int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_PT);
   if(index_sell>WRONG_VALUE)
     {
      COrder* sell=list_sell.At(index_sell);
      if(sell!=NULL)
        {
         //--- Si la orden está puesta por encima del precio (SellLimit), y debe ser "bajada" por detrás del precio
         if(sell.TypeOrder()==ORDER_TYPE_SELL_LIMIT)
           {
            //--- Calcular el nuevo precio de ajuste de la orden y los niveles de stop a partir del nuevo precio
            double price=NormalizeDouble(tick.bid+trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Si el precio calculado es superior a la distancia StopLevel, se pospone desde el precio de ajuste de la orden Bid (se observa la distancia StopLevel)
            if(price>tick.bid+stop_level) 
              {
               //--- Si el precio calculado es inferior al paso de arrastre aplazado del precio de fijación de la orden - modifique el precio de fijación de la orden
               if(price<sell.PriceOpen()-trailing_step)
                 {
                  engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1);
                 }
              }
           }
         //--- Si la orden está fijada por debajo del precio (SellStop y SellStopLimit) y debe ser "levantada" por detrás del precio
         else
           {
            //--- Calcular el nuevo precio de ajuste de la orden y los niveles de stop a partir del nuevo precio
            double price=NormalizeDouble(tick.bid-trailing_stop,Digits());
            double sl=(sell.StopLoss()>0 ? NormalizeDouble(price+(sell.StopLoss()-sell.PriceOpen()),Digits()) : 0);
            double tp=(sell.TakeProfit()>0 ? NormalizeDouble(price-(sell.PriceOpen()-sell.TakeProfit()),Digits()) : 0);
            //--- Si el precio calculado es inferior a la distancia StopLevel, se pospone desde el precio de ajuste de la orden Bid (se observa la distancia StopLevel)
            if(price<tick.bid-stop_level) 
              {
               //--- Si el precio calculado es superior al paso de arrastre aplazado del precio de fijación de la orden - modifique el precio de fijación de la orden
               if(price>sell.PriceOpen()+trailing_step)
                 {
                  engine.ModifyOrder((ulong)sell.Ticket(),price,sl,tp,-1);
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+