Modificar una orden pendiente

MetaTrader 5 permite modificar ciertas propiedades de una orden pendiente, incluyendo el precio de activación, los niveles de protección y la fecha de vencimiento. Las propiedades principales, como el tipo de orden o el volumen, no pueden modificarse. En tales casos, deberá eliminar la orden y sustituirla por otra. El único caso en el que el tipo de orden puede ser modificado por el propio servidor es la activación de una orden stop limitada, que se convierte en la orden Limit correspondiente.

La modificación programática de las órdenes se realiza mediante la operación TRADE_ACTION_MODIFY: es esta constante la que hay que escribir en el campo action de la estructura MqlTradeRequest antes de enviarlo al servidor mediante la función OrderSend o OrderSendAsync. El ticket de la orden modificada se indica en el campo order. Teniendo en cuenta action y order, la lista completa de campos obligatorios para esta operación incluye:

  • action
  • order
  • price
  • type_time (el valor por defecto 0 corresponde a ORDER_TIME_GTC)
  • expiration (por defecto 0, no importante para ORDER_TIME_GTC)
  • type_filling (por defecto 0 corresponde a ORDER_FILLING_FOK)
  • stoplimit (sólo para órdenes de los tipos ORDER_TYPE_BUY_STOP_LIMIT y ORDER_TYPE_SELL_STOP_LIMIT)

Campos opcionales:

  • sl
  • tp

Si ya se han establecido niveles de protección para la orden, deben especificarse para que puedan guardarse. Los valores cero indican la supresión de Stop Loss y/o Take Profit.

En la estructura MqlTradeRequestSync (MqlTradeSync.mqh), la implementación de la modificación de la orden se sitúa en el método modify.

struct MqlTradeRequestSyncpublic MqlTradeRequest
{
   ...
   bool modify(const ulong ticket,
      const double pconst double stop = 0const double take = 0,
      ENUM_ORDER_TYPE_TIME duration = ORDER_TIME_GTCdatetime until = 0,
      const double origin = 0)
   {
      if(!OrderSelect(ticket)) return false;
      
      action = TRADE_ACTION_MODIFY;
      order = ticket;
      
      // the following fields are needed for checks inside subfunctions
      type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
      symbol = OrderGetString(ORDER_SYMBOL);
      volume = OrderGetDouble(ORDER_VOLUME_CURRENT);
      
      if(!setVolumePrices(volumepstoptakeorigin)) return false;
      if(!setExpiration(durationuntil)) return false;
      ZeroMemory(result);
      return OrderSend(thisresult);
   }

La ejecución real de la solicitud se realiza de nuevo en el método completed, en la rama dedicada del operador if.

   bool completed()
   {
      ...
      else if(action == TRADE_ACTION_MODIFY)
      {
         result.order = order;
         result.bid = sl;
         result.ask = tp;
         result.price = price;
         result.volume = stoplimit;
         return result.modified(timeout);
      }
      ...
   }

Para que la estructura MqlTradeResultSync conozca los nuevos valores de las propiedades de la orden editada y pueda compararlos con el resultado, los escribimos en campos libres (no son rellenados por el servidor en este tipo de solicitud). Además, en el método modified, la estructura resultante está a la espera de que se aplique la modificación.

struct MqlTradeResultSyncpublic MqlTradeResult
{
   ...
   bool modified(const ulong msc = 1000)
   {
      if(retcode != TRADE_RETCODE_DONE && retcode != TRADE_RETCODE_PLACED)
      {
         return false;
      }
   
      if(!wait(orderModifiedmsc))
      {
         Print("Order not found in environment: #" + (string)order);
         return false;
      }
      return true;
   }
   
   static bool orderModified(MqlTradeResultSync &ref)
   {
      if(!(OrderSelect(ref.order) || HistoryOrderSelect(ref.order)))
      {
         Print("OrderSelect failed: #=" + (string)ref.order);
         return false;
      }
      return TU::Equal(ref.bidOrderGetDouble(ORDER_SL))
         && TU::Equal(ref.askOrderGetDouble(ORDER_TP))
         && TU::Equal(ref.priceOrderGetDouble(ORDER_PRICE_OPEN))
         && TU::Equal(ref.volumeOrderGetDouble(ORDER_PRICE_STOPLIMIT));
   }

Aquí vemos cómo se leen las propiedades de orden utilizando la función OrderGetDouble y se compara con los valores especificados. Todo esto sucede según el procedimiento ya conocido, en un bucle dentro de la función wait, dentro de un tiempo de espera determinado de msc (1000 milisegundos por defecto).

Como ejemplo, vamos a utilizar el Asesor Experto PendingOrderModify.mq5, mientras heredamos algunos fragmentos de código de PendingOrderSend.mq5. En concreto, un conjunto de parámetros de entrada y la función PlaceOrder para crear una nueva orden. Se utiliza en el primer lanzamiento si no hay ninguna orden para la combinación dada del símbolo y número Magic, asegurando así que el Asesor Experto tiene algo que modificar.

Se necesitaba una nueva función para encontrar una orden adecuada: GetMyOrder. Es muy similar a la función GetMyPosition, que se utilizó en el ejemplo con seguimiento de posición (TrailingStop.mq5) para encontrar una posición adecuada. La finalidad de las funciones integradas en la API de MQL5 que se utilizan en GetMyOrder debería quedar clara a partir de sus nombres, y la descripción técnica se presentará en secciones independientes.

ulong GetMyOrder(const string nameconst ulong magic)
{
   for(int i = 0i < OrdersTotal(); ++i)
   {
      ulong t = OrderGetTicket(i);
      if(OrderGetInteger(ORDER_MAGIC) == magic
         && OrderGetString(ORDER_SYMBOL) == name)
      {
         return t;
      }
   }
   
   return 0;
}

Ahora falta el parámetro de entrada Distance2SLTP. En su lugar, el nuevo Asesor Experto calculará automáticamente el rango diario de precios y colocará niveles de protección a una distancia de la mitad de este rango. Al comienzo de cada día, se recalcularán el rango y los nuevos niveles de los campos sl y tp. Las solicitudes de modificación de órdenes se generarán en función de los nuevos valores.

Las órdenes pendientes que se activen y se conviertan en posiciones se cerrarán al alcanzar Stop Loss o Take Profit. El terminal puede informar al programa MQL sobre la activación de órdenes pendientes y el cierre de posiciones si usted describe en él manejadores de evento de trading. Esto permitiría, por ejemplo, evitar la creación de una nueva orden si existe una posición abierta. No obstante, también puede utilizarse la estrategia actual. Así pues, nos ocuparemos de los eventos más adelante.

La lógica principal del Asesor Experto se implementa en el manejador OnTick.

void OnTick()
{
   static datetime lastDay = 0;
   static const uint DAYLONG = 60 * 60 * 24// number of seconds in a day
   //discard the "fractional" part, i.e. time
   if(TimeTradeServer() / DAYLONG * DAYLONG == lastDayreturn;
   ...

Dos líneas al principio de la función garantizan que el algoritmo se ejecute una vez al principio de cada día. Para ello, calculamos la fecha actual sin hora y la comparamos con el valor de la variable lastDay que contiene la última fecha correcta. Por supuesto, el estado de éxito o error queda claro al final de la función, así que volveremos a ello más adelante.

A continuación, se calcula el rango de precios del día anterior.

   const string symbol = StringLen(Symbol) == 0 ? _Symbol : Symbol;
   const double range = iHigh(symbolPERIOD_D11) - iLow(symbolPERIOD_D11);
   Print("Autodetected daily range: ", (float)range);
   ...

Dependiendo de si existe o no una orden en la función GetMyOrder, crearemos una nueva orden a través de PlaceOrder o editaremos la existente utilizando ModifyOrder.

   uint retcode = 0;
   ulong ticket = GetMyOrder(symbolMagic);
   if(!ticket)
   {
      retcode = PlaceOrder((ENUM_ORDER_TYPE)TypesymbolVolume,
         rangeExpirationUntilMagic);
   }
   else
   {
      retcode = ModifyOrder(ticketrangeExpirationUntil);
   }
   ...

Ambas funciones, PlaceOrder y ModifyOrder, trabajan sobre la base de los parámetros de entrada del Asesor Experto y el rango de precios encontrado. Devuelven el estado de la solicitud, que habrá que analizar de alguna manera para decidir qué acción tomar:

  • Actualizar la variable lastDay si la solicitud tiene éxito (la orden se ha actualizado y el Asesor Experto duerme hasta el comienzo del día siguiente).
  • Dejar el día anterior en lastDay durante algún tiempo para volver a intentarlo en los próximos ticks si hay problemas temporales (por ejemplo, la sesión de trading aún no ha comenzado).
  • Detener el Asesor Experto si se detectan problemas graves (por ejemplo, el tipo de orden seleccionada o la dirección de la operación no están permitidos en el símbolo).

   ...
   if(/* some kind of retcode analysis */)
   {
      lastDay = TimeTradeServer() / DAYLONG * DAYLONG;
   }
}

En la sección Cierre de una posición: total y parcial hemos utilizado un análisis simplificado con la macro IS_TANGIBLE, que daba una respuesta en las categorías «sí» y «no» para indicar si había error o no. Evidentemente, este planteamiento debe mejorarse, y volveremos sobre este tema próximamente. Por ahora, nos centraremos en la funcionalidad principal del Asesor Experto.

El código fuente de la función PlaceOrder prácticamente no ha cambiado con respecto al ejemplo anterior. ModifyOrder se muestra a continuación.

Recordemos que determinamos la localización de las órdenes en función del rango diario, al que se aplicó la tabla de coeficientes. El principio no ha cambiado; sin embargo, dado que ahora tenemos dos funciones que trabajan con órdenes, PlaceOrder y ModifyOrder, la tabla Coefficients se sitúa en un contexto global. No lo repetiremos aquí y pasaremos directamente a la función ModifyOrder.

uint ModifyOrder(const ulong ticketconst double range,
   ENUM_ORDER_TYPE_TIME expirationdatetime until)
{
   // default values
   const string symbol = OrderGetString(ORDER_SYMBOL);
   const double point = SymbolInfoDouble(symbolSYMBOL_POINT);
   ...

Los niveles de precios se calculan en función del tipo de orden y del rango pasado.

   const ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
   const double price = TU::GetCurrentPrice(typesymbol) + range * Coefficients[type];
   
   // origin is filled only for orders *_STOP_LIMIT
   const bool stopLimit =
      type == ORDER_TYPE_BUY_STOP_LIMIT ||
      type == ORDER_TYPE_SELL_STOP_LIMIT;
   const double origin = stopLimit ? TU::GetCurrentPrice(typesymbol) : 0
   
   TU::TradeDirection dir(type);
   const int sltp = (int)(range / 2 / point);
   const double stop = sltp == 0 ? 0 :
      dir.negative(stopLimit ? origin : pricesltp * point);
   const double take = sltp == 0 ? 0 :
      dir.positive(stopLimit ? origin : pricesltp * point);
   ...

Después de calcular todos los valores, creamos un objeto de la estructura MqlTradeRequestSync y ejecutamos la solicitud.

   MqlTradeRequestSync request(symbol);
   
   ResetLastError();
   // pass the data for the fields, send the order and wait for the result
   if(request.modify(ticketpricestoptakeexpirationuntilorigin)
      && request.completed())
   {
      Print("OK order modified: #="ticket);
   }
   
   Print(TU::StringOf(request));
   Print(TU::StringOf(request.result));
   return request.result.retcode;
}

Para analizar retcode que tenemos que ejecutar en el bloque de llamada dentro de OnTick, se desarrolló un nuevo mecanismo que complementaba el archivo TradeRetcode.mqh. Todos los códigos de devolución del servidor se dividen en varios grupos de «gravedad», descritos por los elementos de la enumeración TRADE_RETCODE_SEVERITY.

enum TRADE_RETCODE_SEVERITY
{
   SEVERITY_UNDEFINED,   // something non-standard - just output to the log
   SEVERITY_NORMAL,      // normal operation
   SEVERITY_RETRY,       // try updating environment/prices again (probably several times) 
   SEVERITY_TRY_LATER,   // we should wait and try again
   SEVERITY_REJECT,      // request denied, probably(!) you can try again
                         // 
   SEVERITY_INVALID,     // need to fix the request
   SEVERITY_LIMITS,      // need to check the limits and fix the request
   SEVERITY_PERMISSIONS// it is required to notify the user and change the program/terminal settings
   SEVERITY_ERROR,       // stop, output information to the log and to the user
};

De forma simplista, la primera mitad corresponde a errores recuperables: suele bastar con esperar un poco y reintentar la solicitud. La segunda mitad requiere que cambie el contenido de la solicitud, compruebe la configuración de la cuenta o del símbolo, los permisos para el programa y, en el peor de los casos, que deje de operar. Aquellos que lo deseen pueden dibujar una línea separadora condicional, no después de SEVERITY_REJECT, como se resalta visualmente ahora, sino antes.

La división de todos los códigos en grupos se realiza mediante la función TradeCodeSeverity (indicada con abreviaturas).

TRADE_RETCODE_SEVERITY TradeCodeSeverity(const uint retcode)
{
   static const TRADE_RETCODE_SEVERITY severities[] =
   {
      ...
      SEVERITY_RETRY,       // REQUOTE (10004)
      SEVERITY_UNDEFINED,     
      SEVERITY_REJECT,      // REJECT (10006)
      SEVERITY_NORMAL,      // CANCEL (10007)
      SEVERITY_NORMAL,      // PLACED (10008)
      SEVERITY_NORMAL,      // DONE (10009)
      SEVERITY_NORMAL,      // DONE_PARTIAL (10010)
      SEVERITY_ERROR,       // ERROR (10011)
      SEVERITY_RETRY,       // TIMEOUT (10012)
      SEVERITY_INVALID,     // INVALID (10013)
      SEVERITY_INVALID,     // INVALID_VOLUME (10014)
      SEVERITY_INVALID,     // INVALID_PRICE (10015)
      SEVERITY_INVALID,     // INVALID_STOPS (10016)
      SEVERITY_PERMISSIONS// TRADE_DISABLED (10017)
      SEVERITY_TRY_LATER,   // MARKET_CLOSED (10018)
      SEVERITY_LIMITS,      // NO_MONEY (10019)
      ...
   };
   
   if(retcode == 0return SEVERITY_NORMAL;
   if(retcode < 10000 || retcode > HEDGE_PROHIBITEDreturn SEVERITY_UNDEFINED;
   return severities[retcode - 10000];
}

Gracias a esta funcionalidad, el manejador OnTick puede complementarse con un tratamiento de errores «inteligente». Una variable estática RetryFrequency almacena la frecuencia con la que el programa intentará repetir la solicitud en caso de errores no críticos. La última vez que se realizó un intento de este tipo se almacenó en la variable RetryRecordTime.

void OnTick()
{
   ...
   const static int DEFAULT_RETRY_TIMEOUT = 1// seconds
   static int RetryFrequency = DEFAULT_RETRY_TIMEOUT;
   static datetime RetryRecordTime = 0;
   if(TimeTradeServer() - RetryRecordTime < RetryFrequencyreturn;
   ...

Una vez que la función PlaceOrder o ModifyOrder devuelve el valor de retcode, nos enteramos de su gravedad y, en función de la misma, elegimos una de las tres alternativas: detener el Asesor Experto, esperar un tiempo u operar de forma regular (marcando la modificación exitosa de la orden por el día actual en lastDay).

   const TRADE_RETCODE_SEVERITY severity = TradeCodeSeverity(retcode);
   if(severity >= SEVERITY_INVALID)
   {
      Alert("Can't place/modify pending order, EA is stopped");
      RetryFrequency = INT_MAX;
   }
   else if(severity >= SEVERITY_RETRY)
   {
      RetryFrequency += (int)sqrt(RetryFrequency + 1);
      RetryRecordTime = TimeTradeServer();
      PrintFormat("Problems detected, waiting for better conditions "
         "(timeout enlarged to %d seconds)",
         RetryFrequency);
   }
   else
   {
      if(RetryFrequency > DEFAULT_RETRY_TIMEOUT)
      {
         RetryFrequency = DEFAULT_RETRY_TIMEOUT;
         PrintFormat("Timeout restored to %d second"RetryFrequency);
      }
      lastDay = TimeTradeServer() / DAYLONG * DAYLONG;
   }

En caso de problemas repetidos clasificados como solucionables, el tiempo de espera de RetryFrequency aumenta gradualmente con cada error subsiguiente, pero se restablece a 1 segundo cuando la solicitud se procesa correctamente.

Cabe señalar que los métodos de la estructura aplicada MqlTradeRequestSync comprueban la corrección de un gran número de combinaciones de parámetros y, si se encuentran problemas, interrumpen el proceso antes de la llamada a SendRequest. Este comportamiento está activado por defecto, pero puede desactivarse definiendo una macro RETURN(X) vacía antes de la directiva #include con MqlTradeSync.mqh.

#define RETURN(X)
#include <MQL5Book/MqlTradeSync.mqh>

Con esta definición de macro, las comprobaciones no sólo imprimirán advertencias en el registro sino que seguirán ejecutando métodos hasta la llamada a SendRequest.

En cualquier caso, tras llamar a uno u otro método de la estructura MqlTradeResultSync, el código de error se añadirá a retcode. Esto lo hará el servidor o los algoritmos de comprobación de la estructura MqlTradeRequestSync (aquí utilizamos el hecho de que la instancia MqlTradeResultSync está incluida dentro de MqlTradeRequestSync). No se proporciona aquí la descripción de la devolución de códigos de error y el uso de la macro RETURN en los métodos MqlTradeRequestSync en aras de la brevedad. Los interesados pueden ver el código fuente completo en el archivo MqlTradeSync.mqh.

Vamos a ejecutar el Asesor Experto PendingOrderModify.mq5 en el probador, con el modo visual activado, utilizando los datos de XAUUSD, H1 (todos los ticks o el modo de ticks reales). Con la configuración por defecto, el Asesor Experto colocará órdenes del tipo ORDER_TYPE_BUY_STOP con un lote mínimo. Comprobemos en el registro y en el historial de operaciones que el programa coloca órdenes pendientes y las modifica al principio de cada día.

2022.01.03 01:05:00 Autodetected daily range: 14.37

2022.01.03 01:05:00 buy stop 0.01 XAUUSD at 1845.73 sl: 1838.55 tp: 1852.91 (1830.63 / 1831.36)

2022.01.03 01:05:00 OK order placed: #=2

2022.01.03 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »

» @ 1845.73, SL=1838.55, TP=1852.91, ORDER_TIME_GTC, M=1234567890

2022.01.03 01:05:00 DONE, #=2, V=0.01, Bid=1830.63, Ask=1831.36, Request executed

2022.01.04 01:05:00 Autodetected daily range: 33.5

2022.01.04 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1836.56]

2022.01.04 01:05:00 OK order modified: #=2

2022.01.04 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »

» @ 1836.56, SL=1819.81, TP=1853.31, ORDER_TIME_GTC, #=2

2022.01.04 01:05:00 DONE, #=2, @ 1836.56, Bid=1819.81, Ask=1853.31, Request executed, Req=1

2022.01.05 01:05:00 Autodetected daily range: 18.23

2022.01.05 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1832.56]

2022.01.05 01:05:00 OK order modified: #=2

2022.01.05 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »

» @ 1832.56, SL=1823.45, TP=1841.67, ORDER_TIME_GTC, #=2

2022.01.05 01:05:00 DONE, #=2, @ 1832.56, Bid=1823.45, Ask=1841.67, Request executed, Req=2

...

2022.01.11 01:05:00 Autodetected daily range: 11.96

2022.01.11 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1812.91]

2022.01.11 01:05:00 OK order modified: #=2

2022.01.11 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »

» @ 1812.91, SL=1806.93, TP=1818.89, ORDER_TIME_GTC, #=2

2022.01.11 01:05:00 DONE, #=2, @ 1812.91, Bid=1806.93, Ask=1818.89, Request executed, Req=6

2022.01.11 18:10:58 order [#2 buy stop 0.01 XAUUSD at 1812.91] triggered

2022.01.11 18:10:58 deal #2 buy 0.01 XAUUSD at 1812.91 done (based on order #2)

2022.01.11 18:10:58 deal performed [#2 buy 0.01 XAUUSD at 1812.91]

2022.01.11 18:10:58 order performed buy 0.01 at 1812.91 [#2 buy stop 0.01 XAUUSD at 1812.91]

2022.01.11 20:28:59 take profit triggered #2 buy 0.01 XAUUSD 1812.91 sl: 1806.93 tp: 1818.89 »

» [#3 sell 0.01 XAUUSD at 1818.89]

2022.01.11 20:28:59 deal #3 sell 0.01 XAUUSD at 1818.91 done (based on order #3)

2022.01.11 20:28:59 deal performed [#3 sell 0.01 XAUUSD at 1818.91]

2022.01.11 20:28:59 order performed sell 0.01 at 1818.91 [#3 sell 0.01 XAUUSD at 1818.89]

2022.01.12 01:05:00 Autodetected daily range: 23.28

2022.01.12 01:05:00 buy stop 0.01 XAUUSD at 1843.77 sl: 1832.14 tp: 1855.40 (1820.14 / 1820.49)

2022.01.12 01:05:00 OK order placed: #=4

2022.01.12 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »

» @ 1843.77, SL=1832.14, TP=1855.40, ORDER_TIME_GTC, M=1234567890

2022.01.12 01:05:00 DONE, #=4, V=0.01, Bid=1820.14, Ask=1820.49, Request executed, Req=7

La orden puede activarse en cualquier momento, tras lo cual la posición se cierra al cabo de un tiempo mediante stop loss o take profit (como en el código anterior).

En algunos casos, puede darse la situación de que la posición siga existiendo al comienzo del día siguiente, y entonces se creará una nueva orden además de ésta, como en la captura de pantalla siguiente:

El experto con una estrategia de trading en órdenes pendientes en un probador

El Asesor Experto con una estrategia de trading basada en órdenes pendientes en el probador

Tenga en cuenta que, debido al hecho de que solicitamos cotizaciones del marco temporal PERIOD_D1 para calcular el rango diario, el probador visual abre el gráfico correspondiente, además del gráfico de trabajo actual. Este servicio funciona no sólo para marcos temporales distintos del de trabajo, sino también para otros símbolos. Esto será útil, en particular, a la hora de desarrollar Asesores expertos multidivisa.

Para comprobar cómo funciona el tratamiento de errores, pruebe a desactivar el trading para el Asesor Experto. El registro contendrá lo siguiente:

Autodetected daily range: 34.48
TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, »
  » @ 1975.73, SL=1958.49, TP=1992.97, ORDER_TIME_GTC, M=1234567890
CLIENT_DISABLES_AT, AutoTrading disabled by client
Alert: Can't place/modify pending order, EA is stopped

Este error es crítico, y el Asesor Experto deja de funcionar.

Para demostrar uno de los errores más sencillos, podríamos utilizar el manejador OnTimer en lugar de OnTick. Entonces, lanzar el mismo Asesor Experto en símbolos en los que las sesiones de trading duran sólo una parte del día generaría periódicamente una secuencia de errores no críticos sobre un mercado cerrado («Market closed»). En este caso, el Asesor Experto seguiría intentando comenzar a operar, aumentando constantemente el tiempo de espera.

Esto, en particular, es fácil de comprobar en el probador, que permite establecer sesiones de trading arbitrarias para cualquier símbolo. En la pestaña Settings, a la derecha de la lista desplegable Delays, hay un botón que abre el cuadro de diálogo Trade setup. Allí deberá incluir la opción Use your settings y, en la pestaña Trade, añadir al menos un registro a la tabla Non-trading periods.

Establecer periodos no de trading en el probador

Establecer periodos no de trading en el probador

Tenga en cuenta que lo que se configura aquí son los periodos no de trading, no las sesiones de trading; es decir, este ajuste actúa exactamente al revés en comparación con la especificación del símbolo.

Muchos errores potenciales relacionados con las restricciones de trading pueden eliminarse mediante un análisis preliminar del entorno utilizando una clase como Permissions presentada en la sección Restricciones y permisos para las transacciones de la cuenta.