English Русский 中文 Deutsch 日本語
preview
Algoritmos avanzados de ejecución de órdenes en MQL5: TWAP, VWAP y órdenes Iceberg

Algoritmos avanzados de ejecución de órdenes en MQL5: TWAP, VWAP y órdenes Iceberg

MetaTrader 5Sistemas comerciales |
36 6
N Soumik
N Soumik
  1. Introducción
  2. Entender los algoritmos de ejecución
  3. Implementación en MQL5
  4. Implementación del analizador de rendimiento
  5. Comparación del rendimiento de los algoritmos
  6. Integración de algoritmos de ejecución con estrategias comerciales
  7. Ejemplos de integración
  8. Integración de estrategias
  9. Resultados de las pruebas retrospectivas
  10. Conclusión



Introducción

Imagina que estás de pie al borde de la sala de operaciones, con el corazón latiendo con fuerza mientras los precios cambian en tiempo real. Un movimiento en falso, un pedido excesivo, y tu ventaja se esfuma en un instante. Bienvenido al mundo en el que la calidad de la ejecución no es solo algo deseable, sino el arma secreta que separa a los ganadores del resto.

Durante décadas, los pesos pesados institucionales han utilizado discretamente sofisticados algoritmos para dividir, fragmentar y desplegar sigilosamente sus órdenes, todo ello con el fin de evitar desviaciones y controlar el impacto en el mercado. Ahora, gracias a la flexibilidad de MQL5, ese mismo potente manual de estrategias está al alcance de todos los operadores minoristas ambiciosos.

¿Por qué tanto escándalo?

Imagínate esto: ves una oportunidad de oro y decides apostar fuerte. Usted ejecuta una orden de mercado por el tamaño total, solo para ver cómo el precio se desploma bajo el peso de su propia operación. En cuestión de segundos, tu entrada ideal se convierte en un compromiso inestable. Esa es la famosa desventaja del impacto en el mercado, y se nota incluso en los mercados más líquidos.

Los algoritmos de ejecución son tu antídoto. Al dividir un pedido grande en una secuencia de partes más pequeñas y estratégicamente sincronizadas, suavizan su huella en el libro de pedidos. ¿El resultado? Menos deslizamientos, ejecuciones más ajustadas y una mejora general en su precio medio de ejecución.

De la teoría al trading

«Claro», dirás encogiéndote de hombros, «pero yo no muevo sumas institucionales». Aquí está la clave: no tienes por qué hacerlo. Tanto si está desplegando medio lote como un puñado de minilotes, la volatilidad puede seguir afectando a su ejecución. Estas herramientas te ayudan a:

  • Control del deslizamiento: Incluso las órdenes modestas pueden variar en mercados volátiles.
  • Afilá tu ventaja: Las ejecuciones por capas suelen ofrecerte un precio medio más favorable que una apuesta única.
  • Mantén la calma: Los flujos de trabajo automatizados eliminan la tentación de comprar o vender de forma precipitada.
  • Escalabilidad sin fisuras: A medida que su cuenta crece, la ejecución sigue siendo ágil, independientemente del volumen de sus órdenes.
  • Pasa desapercibido: Las órdenes Iceberg, en particular, ocultan el tamaño real de tu orden, lo que mantiene a los algoritmos entrometidos en la incertidumbre.

El panorama democratizado actual significa que la misma tecnología de ejecución que antes requería presupuestos multimillonarios ahora puede funcionar en su estación de operaciones personal. Al incorporar el código MQL5 optimizado para las estrategias TWAP, VWAP e Iceberg en su plataforma, se dotará de la potencia institucional sin abandonar el ámbito minorista.

Prepárate para darle un giro radical a tu proceso de ejecución. El juego está cambiando, y con estos algoritmos en tu arsenal, jugarás para ganar.


Entender los algoritmos de ejecución

Antes de profundizar en los detalles de la implementación, es esencial comprender la teoría que subyace a cada algoritmo de ejecución y por qué son eficaces en diferentes escenarios de mercado.

  1. Precio medio ponderado por tiempo (Time-Weighted Average Price, TWAP): El TWAP es un algoritmo de ejecución sencillo que divide una orden grande en partes iguales y las envía a intervalos de tiempo fijos durante un período determinado. Su objetivo es igualar el precio medio del instrumento durante ese tiempo.
    • Cómo funciona:

      • Envía órdenes a intervalos de tiempo regulares entre el inicio y el final.
      • Generalmente se utilizan órdenes de igual tamaño (aunque puedes agregarle aleatoriedad a los tamaños).
      • Sigue un calendario predeterminado, independientemente de los movimientos de precios.
      • Distribuye el impacto del mercado de manera uniforme a lo largo del tiempo para mantener bajo el deslizamiento.

    • Cuándo utilizarlo:

      • Necesitas un precio medio de ejecución durante un periodo de tiempo específico.
      • La liquidez se mantiene estable durante todo el período de negociación.
      • Tienes un plazo determinado para completar tu orden.
      • Prefieres un enfoque sencillo y predecible.

  2. Precio medio ponderado por volumen (Volume-Weighted Average Price, VWAP): El VWAP mejora el TWAP al ponderar los tamaños de las órdenes según el volumen esperado. En lugar de tramos iguales, envía operaciones más grandes cuando el volumen tiende a ser mayor.
    • Cómo funciona:

      • Asigna el tamaño de la orden en proporción a los patrones históricos de volumen.
      • Analiza los volúmenes de negociación pasados para predecir la distribución futura del volumen.
      • Puede adaptarse a cambios de volumen en tiempo real en algunas implementaciones.
      • Se ejecuta más durante los periodos de gran volumen para reducir el impacto.

    • Cuándo utilizarlo:

      • Tu rendimiento se mide en función del VWAP.
      • El volumen sigue un patrón diario predecible.
      • Usted está operando en un mercado en el que la liquidez varía a lo largo de la sesión.
      • Quieres alinearte con el flujo natural del mercado.

  3. Órdenes Iceberg: Las órdenes Iceberg se centran en ocultar el tamaño real de una orden grande. Solo se ve una pequeña «punta» en cada momento; una vez que se llena, aparece la siguiente parte.
    • Cómo funciona:

      • Muestra solo una parte del pedido total.
      • Libera nuevos fragmentos visibles después de ejecutar cada consejo.
      • Puede fijar o aleatorizar el tamaño visible para reducir la detección.
      • A menudo se colocan como órdenes limitadas para un mejor control del precio.

    • Cuándo utilizarlo:

      • Debe ocultar el tamaño total de su pedido.
      • El mercado no es muy líquido y las operaciones de gran volumen pueden influir en los precios.
      • Desea mantener la ejecución a un nivel de precio específico.
      • Le preocupa que otros operadores detecten y se adelanten a su orden.


Implementación en MQL5

    Ahora que comprendemos la teoría detrás de estos algoritmos de ejecución, implementémoslos en MQL5. Crearemos un marco modular y orientado a objetos que permita utilizar estos algoritmos de forma individual o combinarlos en un sistema de ejecución unificado.

    Clase base: CExecutionAlgorithm

    Comenzaremos definiendo una clase base que proporcione funcionalidad común para todos los algoritmos de ejecución:

    //+------------------------------------------------------------------+
    //| Base class for all execution algorithms                          |
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;              // Symbol to trade
       double            m_totalVolume;         // Total volume to execute
       double            m_executedVolume;      // Volume already executed
       double            m_remainingVolume;     // Volume remaining to execute
       datetime          m_startTime;           // Start time for execution
       datetime          m_endTime;             // End time for execution
       bool              m_isActive;            // Flag indicating if the algorithm is active
       int               m_totalOrders;         // Total number of orders placed
       int               m_filledOrders;        // Number of filled orders
       double            m_avgExecutionPrice;   // Average execution price
       double            m_executionValue;      // Total value of executed orders
       int               m_slippage;            // Allowed slippage in points
       
    public:
       // Constructor
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage = 3);
       
       // Destructor
       virtual ~CExecutionAlgorithm();
       
       // Common methods
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate();
       
       // Utility methods
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Getters
       string            GetSymbol() const { return m_symbol; }
       double            GetTotalVolume() const { return m_totalVolume; }
       double            GetExecutedVolume() const { return m_executedVolume; }
       double            GetRemainingVolume() const { return m_remainingVolume; }
       datetime          GetStartTime() const { return m_startTime; }
       datetime          GetEndTime() const { return m_endTime; }
       bool              IsActive() const { return m_isActive; }
       int               GetTotalOrders() const { return m_totalOrders; }
       int               GetFilledOrders() const { return m_filledOrders; }
       double            GetAverageExecutionPrice() const { return m_avgExecutionPrice; }
    };
    

    El método PlaceOrder es especialmente importante, ya que se encarga de la ejecución real de la orden y actualiza el seguimiento del volumen:

    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price)
    {
       // Prepare the trade request
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volume;
       m_remainingVolume -= volume;
       UpdateAverageExecutionPrice(price, volume);
       
       // Store the order ticket for future reference
       ulong ticket = result.order;
       
       return true;
    }
    

    Esta función crea y envía una orden de mercado inicializando a cero MqlTradeRequest y MqlTradeResult, rellenando el símbolo, el volumen, el tipo de orden, el precio, el deslizamiento y un número mágico, y luego llamando a OrderSend. Si el envío falla o el código de retorno del bróker no es TRADE_RETCODE_DONE, registra el error y devuelve false. Si tiene éxito, actualiza los contadores internos (recuentos totales/de llenado, volumen ejecutado y restante), recalcula el precio medio, captura el ID del ticket y devuelve el valor true.

    Implementación del TWAP

    El algoritmo TWAP divide el período de ejecución en intervalos de tiempo iguales y coloca órdenes de igual tamaño (o aleatorias) en cada intervalo:

    //+------------------------------------------------------------------+
    //| Time-Weighted Average Price (TWAP) Algorithm                     |
    //+------------------------------------------------------------------+
    class CTWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_intervalVolume;      // Volume per interval
       bool              m_useRandomization;    // Whether to randomize order sizes
       double            m_randomizationFactor; // Factor for randomization (0-1)
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
             double randomizationFactor = 0.2, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // TWAP specific methods
       void              CalculateIntervalVolume();
       datetime          CalculateNextExecutionTime();
       double            GetRandomizedVolume(double baseVolume);
       bool              IsTimeToExecute();
    };
    

    Método clave: CalculateNextExecutionTime

    Este método garantiza que los pedidos se espacien adecuadamente en el tiempo, con un retraso inicial para el primer pedido:
    datetime CTWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
       // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("TWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("TWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Este método divide la ventana desde m_startTime hasta m_endTime en m_intervals segmentos iguales y devuelve cuándo debe activarse la siguiente operación: en la primera llamada es simplemente m_startTime + m_initialDelay, y en cada llamada posterior es TimeCurrent() + el valor de un intervalo en segundos (pero nunca más allá de m_endTime).

    Método Execute:El método Execute comprueba si es el momento de realizar un pedido y se encarga de la realización del pedido propiamente dicha:

    bool CTWAP::Execute()
    {
       if(!m_isActive)
          return false;
       
       // Check if it's time to execute the next order
       if(!IsTimeToExecute())
          return true; // Not time yet
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomization ? 
                               GetRandomizedVolume(m_intervalVolume) : 
                               m_intervalVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       // Get current market price
       double price = 0.0;
       if(m_orderType == ORDER_TYPE_BUY)
          price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
       else
          price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          
       Print("TWAP: Placing order for interval ", m_currentInterval,
             ", Volume: ", DoubleToString(volumeToExecute, 2),
             ", Price: ", DoubleToString(price, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_DEAL;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = price;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("TWAP: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("TWAP: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Update statistics
       m_totalOrders++;
       m_filledOrders++;
       m_executedVolume += volumeToExecute;
       m_remainingVolume -= volumeToExecute;
       
       // Update interval counter
       m_currentInterval++;
       m_firstOrderPlaced = true;
       
       // Calculate the time for the next execution
       if(m_currentInterval < m_intervals && m_remainingVolume > 0)
          m_nextExecutionTime = CalculateNextExecutionTime();
       else
          m_isActive = false; // All intervals completed or no volume left
          
       Print("TWAP: Executed ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(price, _Digits), 
             ". Remaining: ", DoubleToString(m_remainingVolume, 2),
             ", Next execution: ", TimeToString(m_nextExecutionTime));
             
       return true;
    }
    

    Este método Execute gestiona una parte de su ejecución TWAP. En primer lugar, se interrumpe si la estrategia no está activa o si aún no es el momento de operar. Cuando lo hace, selecciona una parte fija o aleatoria del volumen restante (sin superar nunca lo que queda) y, a continuación, busca el precio de venta actual (para compras) o el precio de compra (para ventas). Registra el intervalo, el volumen y el precio, crea una MqlTradeRequest con su símbolo, volumen, tipo, precio, deslizamiento y número mágico, y llama a OrderSend. Si el envío falla o el bróker devuelve cualquier código distinto de TRADE_RETCODE_DONE, se muestra un error y se devuelve false.

    Si tiene éxito, incrementa los contadores de órdenes, ajusta el volumen ejecutado y restante, aumenta el recuento de intervalos, marca que se ha enviado la primera orden y, a continuación, programa la siguiente hora de ejecución o desactiva la estrategia si se han agotado los intervalos o el volumen. Finalmente, registra lo que ha ocurrido y devuelve verdadero.

    Implementación de VWAP

    El algoritmo VWAP es similar al TWAP, pero distribuye los tamaños de las órdenes basándose en patrones de volumen históricos:
    //+------------------------------------------------------------------+
    //| Volume-Weighted Average Price (VWAP) Algorithm                   |
    //+------------------------------------------------------------------+
    class CVWAP : public CExecutionAlgorithm
    {
    private:
       int               m_intervals;           // Number of time intervals
       int               m_currentInterval;     // Current interval
       datetime          m_nextExecutionTime;   // Next execution time
       double            m_volumeProfile[];     // Historical volume profile
       double            m_intervalVolumes[];   // Volume per interval based on profile
       bool              m_adaptiveMode;        // Whether to adapt to real-time volume
       ENUM_ORDER_TYPE   m_orderType;           // Order type (buy or sell)
       int               m_historyDays;         // Number of days to analyze for volume profile
       bool              m_profileLoaded;       // Flag indicating if profile was loaded
       bool              m_firstOrderPlaced;    // Flag to track if first order has been placed
       int               m_initialDelay;        // Initial delay in seconds before first execution
       datetime          m_lastCheckTime;       // Last time order status was checked
       int               m_checkInterval;       // How often to check order status (seconds)
       
    public:
       // Constructor
       CVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
             int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
             bool adaptiveMode = true, int slippage = 3, int initialDelay = 10);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // VWAP specific methods
       bool              LoadVolumeProfile();
       void              CalculateIntervalVolumes();
       void              AdjustToRealTimeVolume();
       datetime          CalculateNextExecutionTime();
       double            GetCurrentVWAP();
       bool              IsTimeToExecute();
    };
    
    Al igual que TWAP, VWAP también implementa el método CalculateNextExecutionTime para garantizar un espaciado adecuado de las órdenes:
    datetime CVWAP::CalculateNextExecutionTime()
    {
       // Calculate the duration of each interval
       int totalSeconds = (int)(m_endTime - m_startTime);
       int intervalSeconds = totalSeconds / m_intervals;
       
          // Calculate the next execution time
       datetime nextTime;
       
       if(m_currentInterval == 0) {
          // First interval - start at the defined start time plus initial delay
          nextTime = m_startTime + m_initialDelay;
          
          Print("VWAP: First execution time calculated with ", m_initialDelay, 
                " seconds delay: ", TimeToString(nextTime));
       } else {
          // For subsequent intervals, ensure proper spacing from current time
          datetime currentTime = TimeCurrent();
          nextTime = currentTime + intervalSeconds;
          
          // Make sure we don't exceed the end time
          if(nextTime > m_endTime)
             nextTime = m_endTime;
             
          Print("VWAP: Next execution time calculated: ", TimeToString(nextTime),
                " (interval: ", intervalSeconds, " seconds)");
       }
       
       return nextTime;
    }
    

    Implementación de órdenes Iceberg

    Las órdenes Iceberg ocultan el tamaño real de una orden, mostrando solo una pequeña parte al mercado en un momento dado:

    //+------------------------------------------------------------------+
    //| Iceberg Order Implementation                                     |
    //+------------------------------------------------------------------+
    class CIcebergOrder : public CExecutionAlgorithm
    {
    private:
       double            m_visibleVolume;          // Visible portion of the order
       double            m_minVisibleVolume;       // Minimum visible volume
       double            m_maxVisibleVolume;       // Maximum visible volume
       bool              m_useRandomVisibleVolume; // Whether to randomize visible volume
       int               m_orderPlacementDelay;    // Delay between order placements (ms)
       bool              m_avoidRoundNumbers;      // Whether to avoid round numbers in price
       double            m_limitPrice;             // Limit price for the orders
       ulong             m_currentOrderTicket;     // Current active order ticket
       ENUM_ORDER_TYPE   m_orderType;              // Order type (buy or sell)
       bool              m_orderActive;            // Flag indicating if an order is currently active
       int               m_priceDeviation;         // Price deviation to avoid round numbers (in points)
       datetime          m_lastCheckTime;          // Last time order status was checked
       int               m_checkInterval;          // How often to check order status (seconds)
       int               m_maxOrderLifetime;       // Maximum lifetime for an order in seconds
       datetime          m_orderPlacementTime;     // When the current order was placed
       
    public:
       // Constructor
       CIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                     double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                     bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                     bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Implementation of virtual methods
       virtual bool      Initialize() override;
       virtual bool      Execute() override;
       virtual bool      Update() override;
       virtual bool      Terminate() override;
       
       // Iceberg specific methods
       double            GetRandomVisibleVolume();
       double            AdjustPriceToAvoidRoundNumbers(double price);
       bool              CheckAndReplaceOrder();
       bool              IsOrderFilled(ulong ticket);
       bool              IsOrderPartiallyFilled(ulong ticket, double &filledVolume);
       bool              IsOrderCancelled(ulong ticket);
       bool              IsOrderExpired(ulong ticket);
       bool              IsOrderTimeout();
       ulong             GetCurrentOrderTicket() { return m_currentOrderTicket; }
       bool              IsOrderActive() { return m_orderActive; }
    };
    
    El método Execute coloca una nueva parte visible de la orden:
    bool CIcebergOrder::Execute()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Execute called but algorithm is not active");
          return false;
       }
          
       // If an order is already active, check its status
       if(m_orderActive)
       {
          Print("Iceberg: Execute called with active order ", m_currentOrderTicket);
          return CheckAndReplaceOrder();
       }
       
       // Calculate the volume for this execution
       double volumeToExecute = m_useRandomVisibleVolume ? 
                               GetRandomVisibleVolume() : 
                               m_visibleVolume;
                               
       // Ensure we don't exceed the remaining volume
       if(volumeToExecute > m_remainingVolume)
          volumeToExecute = m_remainingVolume;
          
       Print("Iceberg: Placing order for ", DoubleToString(volumeToExecute, 2), 
             " at price ", DoubleToString(m_limitPrice, _Digits));
             
       // Place the order using OrderSend directly for more control
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       ZeroMemory(result);
       
       request.action = TRADE_ACTION_PENDING;
       request.symbol = m_symbol;
       request.volume = volumeToExecute;
       request.type = m_orderType;
       request.price = m_limitPrice;
       request.deviation = m_slippage;
       request.magic = 123456; // Magic number for identification
       
       // Send the order
       bool success = OrderSend(request, result);
       
       if(!success)
       {
          Print("Iceberg: OrderSend error: ", GetLastError());
          return false;
       }
       
       // Check the result
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("Iceberg: OrderSend failed with code: ", result.retcode);
          return false;
       }
       
       // Store the order ticket
       m_currentOrderTicket = result.order;
       m_orderActive = true;
       m_orderPlacementTime = TimeCurrent();
       
       Print("Iceberg: Order placed successfully. Ticket: ", m_currentOrderTicket,
             ", Volume: ", DoubleToString(volumeToExecute, 2), 
             ", Remaining: ", DoubleToString(m_remainingVolume, 2));
             
       return true;
    }
    

    Cuando se ejecuta Execute, primero verifica que el algoritmo esté activo. Si ya hay un pedido de Iceberg en curso, llama a CheckAndReplaceOrder() para ver si es necesario cancelarlo o reponerlo. De lo contrario, selecciona una porción visible (fija o aleatoria), la limita al total restante y registra el tamaño y el precio.

    A continuación, crea una solicitud de orden pendiente ( TRADE_ACTION_PENDING ) con el símbolo, el volumen, el precio límite, el deslizamiento y el número mágico, y llama a OrderSend. En caso de error o códigos de retorno no completados, registra y devuelve false; en caso de éxito, guarda el nuevo ticket, marca el pedido como activo, registra la hora de colocación, registra los detalles y devuelve true.

    El método Update incluye la detección del tiempo de espera de los pedidos para garantizar que estos no permanezcan activos indefinidamente:

    bool CIcebergOrder::Update()
    {
       if(!m_isActive)
       {
          Print("Iceberg: Update called but algorithm is not active");
          return false;
       }
          
       // Check if all volume has been executed
       if(m_remainingVolume <= 0)
       {
          Print("Iceberg: All volume executed. Terminating algorithm.");
          return Terminate();
       }
       
       // Check if it's time to check order status
       datetime currentTime = TimeCurrent();
       if(currentTime >= m_lastCheckTime + m_checkInterval)
       {
          m_lastCheckTime = currentTime;
          
          // Log current market conditions
          double currentBid = SymbolInfoDouble(m_symbol, SYMBOL_BID);
          double currentAsk = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          
          Print("Iceberg: Market update - Bid: ", DoubleToString(currentBid, _Digits),
                ", Ask: ", DoubleToString(currentAsk, _Digits),
                ", Limit Price: ", DoubleToString(m_limitPrice, _Digits));
          
          // If an order is active, check its status
          if(m_orderActive)
          {
             // Check if the order has been active too long
             if(IsOrderTimeout())
             {
                Print("Iceberg: Order ", m_currentOrderTicket, " has timed out. Replacing it.");
                
                // Cancel the current order
                if(!CancelOrder(m_currentOrderTicket))
                {
                   Print("Iceberg: Failed to cancel timed out order ", m_currentOrderTicket);
                }
                
                // Reset order tracking
                m_orderActive = false;
                m_currentOrderTicket = 0;
                
                // Place a new order after a delay
                Sleep(m_orderPlacementDelay);
                return Execute();
             }
             
             return CheckAndReplaceOrder();
          }
          else
          {
             // If no order is active, execute a new one
             Print("Iceberg: No active order, executing new order");
             return Execute();
          }
       }
       
       return true;
    }
    

    Periódicamente actualiza las condiciones del mercado y el estado de las órdenes a intervalos definidos por m_checkInterval. Si se ha completado todo el volumen, se termina. De lo contrario, una vez que llega la hora de la comprobación, registra la oferta/demanda actual y el precio límite. Si una orden está activa, comprueba si ha expirado: si ha expirado, la cancela, restablece el estado, permanece inactiva durante m_orderPlacementDelay y se vuelve a ejecutar para colocar una nueva porción; si no ha expirado, se remite a CheckAndReplaceOrder(). Si no hay ningún pedido activo, simplemente llama a Execute para enviar la siguiente parte visible.


    Implementación del analizador de rendimiento

    Nuestra clase de analizador de rendimiento realiza un seguimiento de estas métricas y proporciona métodos para analizar y comparar el rendimiento de los algoritmos:
    //+------------------------------------------------------------------+
    //| Performance Analyzer for Execution Algorithms                    |
    //+------------------------------------------------------------------+
    class CPerformanceAnalyzer
    {
    private:
       string            m_symbol;              // Symbol being analyzed
       datetime          m_startTime;           // Analysis start time
       datetime          m_endTime;             // Analysis end time
       double            m_decisionPrice;       // Price at decision time
       double            m_avgExecutionPrice;   // Average execution price
       double            m_totalVolume;         // Total volume executed
       double            m_implementationShortfall; // Implementation shortfall
       double            m_marketImpact;        // Estimated market impact
       double            m_slippage;            // Average slippage
       int               m_executionTime;       // Total execution time in seconds
       double            m_priceImprovement;    // Total price improvement
       
    public:
       // Constructor
       CPerformanceAnalyzer(string symbol, double decisionPrice);
       
       // Analysis methods
       void              RecordExecution(datetime time, double price, double volume);
       void              CalculateMetrics();
       void              CompareAlgorithms(CPerformanceAnalyzer &other);
       
       // Reporting methods
       void              PrintReport();
       void              SaveReportToFile(string filename);
       
       // Getters
       double            GetImplementationShortfall() const { return m_implementationShortfall; }
       double            GetMarketImpact() const { return m_marketImpact; }
       double            GetSlippage() const { return m_slippage; }
       int               GetExecutionTime() const { return m_executionTime; }
       double            GetPriceImprovement() const { return m_priceImprovement; }
    };
    

    CPerformanceAnalyzer agrupa todas tus métricas post-negociación en un solo lugar. Cuando lo construyes, le asignas un símbolo y el precio de referencia en el momento de la decisión; este marca la hora actual como el inicio. A medida que se completa cada orden secundaria, se llama a RecordExecution(tiempo, precio, volumen), que actualiza los totales acumulados: volumen acumulado, precio medio ponderado de ejecución y marcas de tiempo. Una vez finalizada la estrategia (o periódicamente), se llama a CalculateMetrics(), que calcula:

    • Déficit de implementación (la diferencia entre el precio de su orden y el precio real de ejecución),
    • El deslizamiento medio frente a los precios cotizados,
    • Impacto estimado en el mercado de su actividad,
    • Tiempo total de ejecución (fin menos inicio),
    • Mejora del precio si la hubiera en comparación con los índices de referencia.

    Incluso puedes comparar dos ejecuciones mediante CompareAlgorithms(otherAnalyzer) para ver qué estrategia ha funcionado mejor. Por último, PrintReport() envía las estadísticas clave al registro para su rápida revisión, y SaveReportToFile(nombre de archivo) le permite guardar un informe completo externamente. Los captadores livianos exponen cada métrica para paneles personalizados o análisis adicionales.


    Comparación del rendimiento de los algoritmos

    Las diferentes condiciones del mercado favorecen diferentes algoritmos de ejecución. Aquí hay una comparación general, es decir, una regla general:
    1. TWAP:
      • Ideal para: Mercados estables con liquidez constante.
      • Ventajas: Patrón de ejecución sencillo y predecible.
      • Desventajas: No se adapta a las condiciones cambiantes del mercado.
    2. VWAP:
      • Ideal para: Mercados con patrones de volumen predecibles.
      • Ventajas: Se ajusta al ritmo natural del mercado y, a menudo, permite obtener mejores precios.
      • Desventajas: Requiere datos históricos de volumen, implementación más compleja.
    3. Órdenes Iceberg:
      • Ideal para: Mercados menos líquidos o cuando la sensibilidad al precio es alta.
      • Ventajas: Minimiza el impacto en el mercado, mantiene el control de los precios.
      • Desventajas: El tiempo de ejecución puede ser impredecible, riesgo de ejecución parcial.


    Integración de algoritmos de ejecución con estrategias comerciales

    El verdadero poder de estos algoritmos de ejecución se manifiesta cuando se integran con estrategias de negociación. En esta sección se muestra cómo incorporar nuestros algoritmos de ejecución en sistemas de negociación completos.

    Gestor de ejecución

    Para simplificar la integración, crearemos una clase Execution Manager que sirva como fachada para todos nuestros algoritmos de ejecución:

    //+------------------------------------------------------------------+
    //| Execution Manager - Facade for all execution algorithms          |
    //+------------------------------------------------------------------+
    class CExecutionManager
    {
    private:
       CExecutionAlgorithm* m_algorithm;       // Current execution algorithm
       CPerformanceAnalyzer* m_analyzer;       // Performance analyzer
       
    public:
       // Constructor
       CExecutionManager();
       
       // Destructor
       ~CExecutionManager();
       
       // Algorithm creation methods
       bool              CreateTWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, bool useRandomization = false, 
                                   double randomizationFactor = 0.2, int slippage = 3);
                                   
       bool              CreateVWAP(string symbol, double volume, datetime startTime, datetime endTime, 
                                   int intervals, ENUM_ORDER_TYPE orderType, int historyDays = 5,
                                   bool adaptiveMode = true, int slippage = 3);
                                   
       bool              CreateIcebergOrder(string symbol, double volume, double limitPrice, ENUM_ORDER_TYPE orderType,
                                           double visibleVolume, double minVisibleVolume = 0.0, double maxVisibleVolume = 0.0,
                                           bool useRandomVisibleVolume = true, int orderPlacementDelay = 1000, 
                                           bool avoidRoundNumbers = true, int priceDeviation = 2, int slippage = 3);
       
       // Execution methods
       bool              Initialize();
       bool              Execute();
       bool              Update();
       bool              Terminate();
       
       // Performance analysis
       void              EnablePerformanceAnalysis(double decisionPrice);
       void              PrintPerformanceReport();
       
       // Getters
       CExecutionAlgorithm* GetAlgorithm() { return m_algorithm; }
    };
    

    CExecutionManager actúa como una fachada simple sobre cualquiera de sus algoritmos de ejecución y los vincula en un flujo de trabajo comercial unificado. Internamente, contiene un puntero al CExecutionAlgorithm seleccionado actualmente (TWAP, VWAP o Iceberg) más un CPerformanceAnalyzer para rastrear el rendimiento de sus pedidos.

    Usted elige su estrategia llamando a uno de los métodos Create…, pasando el símbolo, el volumen total, las horas de inicio y fin (para TWAP/VWAP), los conteos de intervalos o tamaños de porción, el tipo de orden y cualquier perilla específica del algoritmo (aleatorización, ventana de historial, precio límite, etc.). Una vez creado, lo ejecuta a través del ciclo de vida habitual:

    1. Initialize() configura cualquier estado o dato que necesites.
    2. Execute() activa la siguiente porción o orden secundaria.
    3. La función Update() completa las encuestas, los datos del mercado o los tiempos de espera.
    4. Terminate() limpia una vez que hayas llenado todo o quieras detenerlo.

    Si habilita el análisis de rendimiento con EnablePerformanceAnalysis(), el administrador registrará los precios de ejecución contra un punto de referencia de decisión y usted puede enviar un informe conciso de pérdidas y ganancias y deslizamiento a través de PrintPerformanceReport(). Siempre puedes obtener el objeto de algoritmo sin procesar con GetAlgorithm() para realizar sondeos o métricas personalizadas.


    Ejemplos de integración

    A continuación se muestran ejemplos de cómo integrar nuestros algoritmos de ejecución con diferentes tipos de estrategias comerciales:

    1. Estrategia de seguimiento de tendencias con ejecución TWAP.

      //+------------------------------------------------------------------+
      //| Trend-Following Strategy with TWAP Execution                     |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int maPeriodFast = 20;
         int maPeriodSlow = 50;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double maFast = iMA(Symbol(), PERIOD_CURRENT, maPeriodFast, 0, MODE_SMA, PRICE_CLOSE, 0);
         double maSlow = iMA(Symbol(), PERIOD_CURRENT, maPeriodSlow, 0, MODE_SMA, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Fast MA crosses above Slow MA
            if(maFast > maSlow)
            {
               // Create TWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateTWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy signal detected. Starting TWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("TWAP execution completed.");
               }
            }
         }
      }
      
    2. Estrategia de reversión a la media con ejecución VWAP.

      //+------------------------------------------------------------------+
      //| Mean-Reversion Strategy with VWAP Execution                      |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int rsiPeriod = 14;
         int rsiOversold = 30;
         int rsiOverbought = 70;
         double volume = 1.0;
         int executionIntervals = 5;
         
         // Calculate indicators
         double rsi = iRSI(Symbol(), PERIOD_CURRENT, rsiPeriod, PRICE_CLOSE, 0);
         
         // Check for entry conditions
         static bool inPosition = false;
         static bool isLong = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: RSI oversold
            if(rsi < rsiOversold)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_BUY))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = true;
                  Print("Buy signal detected. Starting VWAP execution.");
               }
            }
            // Sell signal: RSI overbought
            else if(rsi > rsiOverbought)
            {
               // Create VWAP execution algorithm
               datetime startTime = TimeCurrent();
               datetime endTime = startTime + 3600; // 1 hour execution window
               
               if(executionManager.CreateVWAP(Symbol(), volume, startTime, endTime, 
                                             executionIntervals, ORDER_TYPE_SELL))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  isLong = false;
                  Print("Sell signal detected. Starting VWAP execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("VWAP execution completed.");
               }
            }
            
            // Check for exit conditions
            if(isLong && rsi > rsiOverbought)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
            else if(!isLong && rsi < rsiOversold)
            {
               executionManager.Terminate();
               inPosition = false;
               Print("Exit signal detected. Terminating VWAP execution.");
            }
         }
      }
      
    3. Estrategia de ruptura con órdenes Iceberg.
      //+------------------------------------------------------------------+
      //| Breakout Strategy with Iceberg Orders                            |
      //+------------------------------------------------------------------+
      void OnTick()
      {
         // Strategy parameters
         int channelPeriod = 20;
         double volume = 1.0;
         double visibleVolume = 0.1;
         
         // Calculate indicators
         double upperChannel = iHigh(Symbol(), PERIOD_CURRENT, iHighest(Symbol(), PERIOD_CURRENT, MODE_HIGH, channelPeriod, 1));
         double lowerChannel = iLow(Symbol(), PERIOD_CURRENT, iLowest(Symbol(), PERIOD_CURRENT, MODE_LOW, channelPeriod, 1));
         
         double currentPrice = SymbolInfoDouble(Symbol(), SYMBOL_BID);
         
         // Check for entry conditions
         static bool inPosition = false;
         static CExecutionManager executionManager;
         
         if(!inPosition)
         {
            // Buy signal: Price breaks above upper channel
            if(currentPrice > upperChannel)
            {
               // Create Iceberg Order
               double limitPrice = upperChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_BUY,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Buy breakout detected. Starting Iceberg Order execution.");
               }
            }
            // Sell signal: Price breaks below lower channel
            else if(currentPrice < lowerChannel)
            {
               // Create Iceberg Order
               double limitPrice = lowerChannel; // Place limit order at breakout level
               
               if(executionManager.CreateIcebergOrder(Symbol(), volume, limitPrice, ORDER_TYPE_SELL,
                                                    visibleVolume, visibleVolume * 0.8, visibleVolume * 1.2,
                                                    true, 1000, true, 2, 3))
               {
                  executionManager.Initialize();
                  inPosition = true;
                  Print("Sell breakout detected. Starting Iceberg Order execution.");
               }
            }
         }
         else
         {
            // Update the execution algorithm
            if(executionManager.Update())
            {
               // Check if execution is complete
               if(!executionManager.GetAlgorithm().IsActive())
               {
                  inPosition = false;
                  Print("Iceberg Order execution completed.");
               }
            }
         }
      }
      

    En los tres ejemplos, la integración sigue el mismo patrón de alto nivel:

    1. Mantener el estado

      • Un valor booleano inPosition rastrea si actualmente estás completando un pedido.
      • Un CExecutionManager estático reside en los ticks para administrar el ciclo de vida del algoritmo elegido.

    2. Lógica de entrada

      • En su señal de entrada (cruce de MA, umbral RSI, ruptura de canal), llame al método de creación apropiado en executionManager (TWAP, VWAP o Iceberg), pasando símbolo, volumen total, ventana de tiempo o precio límite, parámetros de segmento y tipo de orden.
      • Si la creación es exitosa, llame inmediatamente a executionManager.Initialize(), establezca inPosition=true y registre su inicio.

    3. Ejecución en curso

      • Mientras inPosition sea true, cada OnTick() invoca executionManager.Update().
      • Dentro de Update(), el gestor llamará internamente a Execute() según sea necesario, sondeará los rellenos, gestionará los tiempos de espera o las actualizaciones del mercado y programará la siguiente porción (o cancelará/sustituirá las órdenes secundarias para Iceberg).

    4. Finalización y salida

      • Después de cada actualización, verifique executionManager.GetAlgorithm()->IsActive(). Una vez que devuelva false (todos los intervalos completados o volumen agotado), establezca inPosition=false y registre que la ejecución ha finalizado.
      • En el ejemplo de reversión a la media VWAP, hay una comprobación de salida adicional: si el precio se revierte más allá de su umbral RSI durante la ejecución, llame a executionManager.Terminate() para detenerlo antes de tiempo.

    Las reglas para cada uno son las siguientes:
    1. Seguimiento de tendencias + TWAP
      Entrada: El cruce rápido de la media móvil por encima de la media móvil lenta activa la señal.Ejecución: Divide su compra de 1 lote en 5 partes iguales durante la siguiente hora.
    2. Reversión a la media + VWAP
      Entrada: RSI < 30 para compras, > 70 para ventas
      Ejecución: Distribuye 1 lote en función del volumen histórico en 5 tramos en 1 hora
      Salida anticipada: Si la señal cambia (por ejemplo, RSI > 70 durante una compra), executionManager.Terminate() aborta las porciones restantes

    3. Rompimiento (Breakout) + Iceberg
      Entrada: El precio rompe el máximo (compra) o el mínimo (venta) del canal.Ejecución: Coloca un límite pendiente al precio de ruptura, revelando solo ~0,1 lotes a la vez y rellenando hasta completar el lote completo de 1.

    Al intercambiar CreateTWAP, CreateVWAP o CreateIcebergOrder, puede conectar cualquier algoritmo de ejecución a su lógica de señales sin duplicar el código repetitivo de gestión de órdenes.


    Integración de estrategias

    Código completo:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    
    // Input parameters
    input int FastMA = 20;                  // Fast moving average period
    input int SlowMA = 50;                  // Slow moving average period
    input double TradingVolume = 0.1;       // Trading volume
    input bool UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int g_maHandle1 = INVALID_HANDLE;
    int g_maHandle2 = INVALID_HANDLE;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit()
    {
       // Initialize execution manager
       g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
       
       // Initialize indicators
       g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
       g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
       
       if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE)
       {
          Print("Failed to create indicator handles");
          return INIT_FAILED;
       }
       
       return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
       // Clean up
       if(g_executionManager != NULL)
       {
          delete g_executionManager;
          g_executionManager = NULL;
       }
       
       // Release indicator handles
       IndicatorRelease(g_maHandle1);
       IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick()
    {
       // Update execution algorithms
       if(g_executionManager != NULL)
          g_executionManager.UpdateAlgorithms();
       
       // Only process at bar open
       if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
          return;
          
       // Get indicator values
       double fastMA[2], slowMA[2];
       
       if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
          CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0)
       {
          Print("Failed to copy indicator buffers");
          return;
       }
       
       // Check for trend signals
       bool buySignal = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
       bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
       
       // Execute signals using the execution manager
       if(buySignal)
       {
          Print("Buy signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_BUY, true);
          }
       }
       else if(sellSignal)
       {
          Print("Sell signal detected");
          
          if(UseAdaptiveExecution)
          {
             // Let the execution manager select the best algorithm
             g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
          }
          else
          {
             // Manually create a TWAP algorithm
             datetime currentTime = TimeCurrent();
             CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime, 
                                                       currentTime + 3600, 6, 
                                                       ORDER_TYPE_SELL, true);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    El asesor experto IntegratedStrategy.mq5 comienza declarando sus metadatos (derechos de autor, enlace, versión) e incluyendo los encabezados de todas nuestras clases de algoritmos de ejecución y el analizador de rendimiento. A continuación, define cuatro entradas ajustables por el usuario: los períodos SMA rápidos y lentos, el volumen total de negociación por señal y un indicador booleano para alternar la ejecución «adaptativa» (donde el gestor decide si utilizar TWAP, VWAP o Iceberg bajo el capó). También se declararon un puntero global a CExecutionManager y dos identificadores de indicadores para que persistan entre ticks.

    En OnInit(), instanciamos el gestor de ejecución, pasándole el símbolo actual, un máximo de tres algoritmos concurrentes y nuestro indicador adaptativo, y luego creamos los dos identificadores del indicador SMA. Si falla cualquiera de los dos identificadores, la inicialización se interrumpe. OnDeinit() simplemente limpia eliminando el administrador y liberando los identificadores del indicador, lo que garantiza que no haya fugas de memoria o identificadores cuando se elimina el EA o se apaga la plataforma.

    La lógica central reside en OnTick(). En primer lugar, llamamos a UpdateAlgorithms() en el gestor de ejecución para que cualquier orden secundaria existente (porciones TWAP, cubos VWAP o tramos Iceberg) se procese, cancele o reponga según sea necesario. A continuación, esperamos una nueva barra (saltándonos si el volumen de ticks sigue aumentando). Una vez que se abre el bar, extraemos los dos últimos valores SMA tanto para los periodos rápidos como para los lentos. Un cruce de abajo hacia arriba activa una señal de compra; lo contrario activa una señal de venta.

    Si la ejecución adaptativa está habilitada, transferimos la señal y el volumen a g_executionManager.ExecuteSignal(), dejando que este elija el algoritmo adecuado. De lo contrario, activamos manualmente una instancia TWAP durante una ventana de una hora y seis segmentos. Este patrón separa claramente la lógica de entrada (detección de tendencias) de la lógica de gestión de órdenes, lo que permite que la misma fachada impulse múltiples estilos de ejecución sin duplicar el código repetitivo.

    Después de incorporar Take Profit, el código cambia a:

    //+------------------------------------------------------------------+
    //|                                  IntegratedStrategy.mq5          |
    //|                        Copyright 2025, MetaQuotes Software Corp. |
    //|                                    https://www.metaquotes.net    |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2025, MetaQuotes Software Corp."
    #property link      "https://www.metaquotes.net"
    #property version   "1.00"
    
    #include "ExecutionAlgorithm.mqh"
    #include "TWAP.mqh"
    #include "VWAP.mqh"
    #include "IcebergOrder.mqh"
    #include "PerformanceAnalyzer.mqh"
    #include "ExecutionManager.mqh"
    #include <Trade\Trade.mqh>
    
    // Input parameters
    input int    FastMA              = 20;    // Fast moving average period
    input int    SlowMA              = 50;    // Slow moving average period
    input double TradingVolume       = 0.1;   // Trading volume
    input bool   UseAdaptiveExecution = true; // Use adaptive execution based on market conditions
    input double EquityTPPercent     = 10.0;  // Equity Take Profit in percent
    input double EquitySLPercent     = 5.0;   // Equity Stop Loss in percent
    
    // Global variables
    CExecutionManager *g_executionManager = NULL;
    int                g_maHandle1       = INVALID_HANDLE;
    int                g_maHandle2       = INVALID_HANDLE;
    double             g_initialEquity   = 0.0;
    CTrade             trade;
    
    //+------------------------------------------------------------------+
    //| Expert initialization function                                   |
    //+------------------------------------------------------------------+
    int OnInit() {
        // Record initial equity
        g_initialEquity = AccountInfoDouble(ACCOUNT_EQUITY);
    
        // Initialize execution manager
        g_executionManager = new CExecutionManager(Symbol(), 3, UseAdaptiveExecution);
    
        // Initialize indicators
        g_maHandle1 = iMA(Symbol(), Period(), FastMA, 0, MODE_SMA, PRICE_CLOSE);
        g_maHandle2 = iMA(Symbol(), Period(), SlowMA, 0, MODE_SMA, PRICE_CLOSE);
    
        if(g_maHandle1 == INVALID_HANDLE || g_maHandle2 == INVALID_HANDLE) {
            Print("Failed to create indicator handles");
            return INIT_FAILED;
        }
    
        return(INIT_SUCCEEDED);
    }
    
    //+------------------------------------------------------------------+
    //| Expert deinitialization function                                 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason) {
        // Clean up
        if(g_executionManager != NULL) {
            delete g_executionManager;
            g_executionManager = NULL;
        }
    
        // Release indicator handles
        IndicatorRelease(g_maHandle1);
        IndicatorRelease(g_maHandle2);
    }
    
    //+------------------------------------------------------------------+
    //| Check equity-based TP and SL, then reset baseline                |
    //+------------------------------------------------------------------+
    void CheckEquityTPandSL() {
        double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
        double tpEquity      = g_initialEquity * (1.0 + EquityTPPercent / 100.0);
        double slEquity      = g_initialEquity * (1.0 - EquitySLPercent / 100.0);
    
        if(currentEquity >= tpEquity) {
            Print("Equity Take Profit reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        } else if(currentEquity <= slEquity) {
            Print("Equity Stop Loss reached: ", currentEquity);
            CloseAllPositions();
            g_initialEquity = currentEquity;
            Print("Equity baseline reset to: ", g_initialEquity);
        }
    }
    
    //+------------------------------------------------------------------+
    //| Close all open positions                                         |
    //+------------------------------------------------------------------+
    void CloseAllPositions() {
        CPositionInfo  m_position;
        CTrade m_trade;
        for(int i = PositionsTotal() - 1; i >= 0; i--) // loop all Open Positions
            if(m_position.SelectByIndex(i)) { // select a position
                m_trade.PositionClose(m_position.Ticket()); // then delete it --period
            }
    }
    
    //+------------------------------------------------------------------+
    //| Expert tick function                                             |
    //+------------------------------------------------------------------+
    void OnTick() {
        // Check and reset equity thresholds
        CheckEquityTPandSL();
    
        // Update execution algorithms
        if(g_executionManager != NULL)
            g_executionManager.UpdateAlgorithms();
    
        // Only process at bar open
        if(iVolume(_Symbol, PERIOD_CURRENT, 0) > 1)
            return;
    
        // Get indicator values
        double fastMA[2], slowMA[2];
    
        if(CopyBuffer(g_maHandle1, 0, 0, 2, fastMA) <= 0 ||
                CopyBuffer(g_maHandle2, 0, 0, 2, slowMA) <= 0) {
            Print("Failed to copy indicator buffers");
            return;
        }
    
        // Check for trend signals
        bool buySignal  = (fastMA[0] > slowMA[0]) && (fastMA[1] <= slowMA[1]);
        bool sellSignal = (fastMA[0] < slowMA[0]) && (fastMA[1] >= slowMA[1]);
    
        // Execute signals using the execution manager
        if(buySignal) {
            Print("Buy signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_BUY, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_BUY, true);
            }
        } else if(sellSignal) {
            Print("Sell signal detected");
    
            if(UseAdaptiveExecution) {
                // Let the execution manager select the best algorithm
                g_executionManager.ExecuteSignal(SIGNAL_TYPE_SELL, TradingVolume);
            } else {
                datetime currentTime = TimeCurrent();
                CTWAP *twap = g_executionManager.CreateTWAP(TradingVolume, currentTime,
                              currentTime + 3600, 6,
                              ORDER_TYPE_SELL, true);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    Resultados de las pruebas retrospectivas

    1. Curvas de riesgo y rentabilidad

    Los escalones verdes de «Balance» muestran el capital contable de su cuenta cada vez que el EA cierra una posición; la línea azul «Equity» suaviza las ganancias y pérdidas no realizadas entre operaciones. Podemos observar una clara tendencia al alza desde enero hasta principios de marzo, con algunos retrocesos, cada uno de los cuales alcanza un máximo de entre el 10 % y el 16 % antes de que la siguiente serie de ganancias recupere el terreno perdido. Ese patrón sugiere que el sistema prospera en condiciones de tendencia, pero aún así sufre caídas tolerables en el valor neto.

    2. Volumen y explotación del riesgo

    En la parte inferior, los triángulos «Deposit Load» (carga de depósito) se reducen gradualmente con el tiempo: este es el tamaño de su posición como porcentaje del capital. Comienza cerca del 10 % de su saldo y se reduce a medida que crece su capital (con un tamaño de volumen fijo), lo que significa que nuestro riesgo por operación disminuye a medida que la cuenta aumenta. Por eso las reducciones se mantienen proporcionalmente similares incluso cuando aumenta su capital en dólares.

    3. Métricas clave de rentabilidad

    • Depósito inicial: 1000 $

    • Beneficio neto: +703 $ (un rendimiento del 70 % en aproximadamente dos meses)

    • Factor de beneficio: 2,34 (ganas 2,34 $ por cada 1 $ perdido)

    • Ganancia esperada: 2,34 dólares por operación de media.

    • Ratio Sharpe: 5,47 (muy alto: fuertes rendimientos ajustados al riesgo)

    Estas cifras nos indican que la estrategia no solo es rentable, sino que genera un margen considerable por encima de su propia volatilidad.

    4. Reducción y recuperación

    • Pérdida máxima: 156 puntos o 9,99 %

    • Pérdida máxima de capital: 228 puntos o 15,89 %.

    • Factor de recuperación: 3,08 (beneficio neto ÷ caída máxima)

    Un factor de recuperación superior a 2 se considera generalmente bueno, por lo que con un 3,08 estás generando más del triple de tu peor pérdida en ganancias.

    5. Distribución comercial

    • Total de operaciones: 300 (600 transacciones, por lo que cada entrada y salida cuenta como dos).

    • Porcentaje de victorias: 76 % (228 victorias frente a 72 derrotas)

    • Ganancia media: 5,39 $

    • Pérdida media: – 7,31 $

    Aunque su tasa de ganancias y su factor de beneficio son sólidos, tenga en cuenta que sus pérdidas son, en promedio, mayores que sus ganancias, algo a tener en cuenta si las condiciones del mercado cambian.

    6. Rachas y consistencia

    • Máximo de ganancias consecutivas: 87 operaciones, +302 $.

    • Máximo de pérdidas consecutivas: 23 operaciones, – 156 $.

    • Racha ganadora promedio: 57 operaciones

    • Racha de pérdidas promedio: 18 operaciones

    Las largas rachas ganadoras impulsan la tendencia al alza, mientras que la racha perdedora más larga solo cuesta alrededor del 15 % del capital.


    Conclusión

    Imagina que eres un operador independiente en un mercado saturado: cada tick cuenta, cada precio de ejecución susurra ganancias o pérdidas. Al incorporar TWAP, VWAP y órdenes Iceberg a su conjunto de herramientas, ya no solo reaccionará ante las fluctuaciones de precios, sino que las coordinará. Estos algoritmos, que antes eran exclusivos de las instituciones, ahora están al alcance de tu mano, cortando la liquidez como un láser y convirtiendo las caóticas carteras de pedidos en oportunidades.

    TWAP se convierte en tu metrónomo constante, regulando tu tamaño de manera uniforme a lo largo de un intervalo establecido, perfecto para cuando la marea está tranquila y simplemente deseas un paseo suave. VWAP te convierte en un experto rastreador de volúmenes, atacando los momentos de mayor actividad bursátil del día y siguiendo el pulso del propio mercado. Y cuando necesitas ocultar tus intenciones, las órdenes Iceberg ocultan tu verdadero tamaño bajo la superficie, revelando solo lo necesario para que se cumpla la orden sin asustar a los grandes operadores.

    Pero estos no son solo trucos aislados. Con nuestro marco modular MQL5, puede incorporarlos a cualquier estrategia (seguidores de tendencias, reversores medios, cazadores de rupturas) con la misma facilidad con la que se coloca una nueva lente. Una única interfaz (ExecutionManager) le permite intercambiar, combinar o incluso superponer algoritmos durante la negociación, mientras que PerformanceAnalyzer lleva la cuenta con gran precisión, midiendo el deslizamiento, el déficit y el impacto en el mercado hasta el último pip.

    ¿Qué sigue? Piensa en la ejecución como un ser vivo que se adapta. Deja que tu TWAP aprenda de los picos de volatilidad. Dirige tus porciones de VWAP a los fondos más profundos. Enseña a tu Iceberg a detectar dónde acechan los depredadores y a esconderse más profundamente. ¿Y por qué detenerse ahí? Incorpore el aprendizaje automático para predecir el microsegundo perfecto para disparar, o combine tipos de órdenes en híbridos personalizados que se adapten a su ventaja competitiva única.

    El mundo del trading nunca se detiene, y tampoco debería hacerlo la ejecución de sus órdenes. Sumérgete, experimenta con audacia y convierte cada corte, cada relleno, en una ventaja calculada. Tu ventaja te espera en el código.

    Para su comodidad, aquí tiene un resumen de los archivos incluidos en este artículo:

    Nombre del archivo Descripción
    ExecutionAlgorithm.mqh Clase base para todos los algoritmos de ejecución. 
    TWAP.mqh Implementación del precio promedio ponderado en el tiempo.
    VWAP.mqh Implementación del precio promedio ponderado por volumen.
    IcebergOrder.mqh Implementación de órdenes Iceberg.
    PerformanceAnalyzer.mqh Herramientas para analizar el rendimiento de la ejecución.
    ExecutionManager.mqh Fachada para una fácil integración con estrategias las comerciales.
    IntegratedStrategy.mq5
    EA de ejemplo que muestra la integración con una estrategia de trading.
    IntegratedStrategy - Take Profit.mq5 EA de ejemplo que muestra la integración con una estrategia de trading con take profit y stop loss en porcentaje sobre el saldo de la cuenta.
    Al incorporar estos algoritmos de ejecución avanzados a su conjunto de herramientas de trading, estará dando un paso importante hacia un trading más profesional y eficiente. Tanto si desea minimizar el impacto de las operaciones de mayor volumen, mejorar sus precios medios de ejecución o simplemente añadir más sofisticación a su enfoque de negociación, estos algoritmos proporcionan soluciones valiosas que pueden mejorar su rendimiento en los competitivos mercados actuales.

    Traducción del inglés realizada por MetaQuotes Ltd.
    Artículo original: https://www.mql5.com/en/articles/17934

    i_vergo
    i_vergo | 16 may 2025 en 07:44
    Gran Artículo
    Prueba de su algo.
    EN el archivo, ExecutionAlgorithm.mqh, añadido este request.type_filling línea = ORDER_FILLING_IOC; en la colocación de la orden para solucionar el problema de colocación de pedidos.
    Probado en M5, abrió sólo 1 operación durante 2 meses, no abrió ninguna orden parcial.
    Probado en H1, nunca aplicó el SL, o TP y todas las operaciones cerraron con pérdidas.

    también durante la compilación genera advertencias
    posible perdida de datos debido a la conversion de tipo de 'long' a 'double' VWAP.mqh 271 41
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' VWAP.mqh 272 22
    posible pérdida de datos debido a la conversión del tipo "long" a "double" VWAP.mqh 449 17
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' PerformanceAnalyzer.mqh 222 17
    Posible pérdida de datos debido a la conversión de tipo "long" a "double" ExecutionManager.mqh 418 17


    sugiera cómo probar el algo,
    Time frame. y cualquier otra recomendación.
    i_vergo
    i_vergo | 16 may 2025 en 07:57
    arreglar los warnins
    también al compilar genera warnings
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' VWAP .mqh 271 41
    posible pérdida de datos debido a la conversión del tipo 'long' a 'double' VWAP .mqh 272 22
    Posible pérdida de datos por conversión de tipo "long" a "double" VWAP .mqh 449 17
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' PerformanceAnalyzer .mqh 222 17
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' ExecutionManager .mqh 418 17


    he cambiado la línea de código
    m_volumeProfile[intervalIndex] += rates[i].tick_volu

    a
    m_volumeProfile[intervalIndex] += (double)rates[i].tick_volume;

    Se fijaron las advertencias
    Ahora necesito que guidence con respecto a mis otras preguntas, como
    marco de tiempo
    y también
    por qué todas las operaciones durante backtest resultado en la pérdida
    cómo probar este gran trabajo de usted ..
    [Eliminado] | 16 may 2025 en 07:57
    i_vergo posible perdida de datos debido a la conversion de tipo de 'long' a 'double' VWAP.mqh 271 41
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' VWAP.mqh 272 22
    posible pérdida de datos debido a la conversión del tipo "long" a "double" VWAP.mqh 449 17
    posible pérdida de datos debido a la conversión de tipo de 'long' a 'double' PerformanceAnalyzer.mqh 222 17
    Posible pérdida de datos debido a la conversión de tipo "long" a "double" ExecutionManager.mqh 418 17


    sugiera cómo probar el algo,
    Time frame. y cualquier otra recomendación.

    Las advertencias no son el problema pero podrían arreglarse rápidamente. Pero sí sería genial si el autor pudiera mostrar paso a paso qué ajustes y entradas utilizó para el backtest.

    CapeCoddah
    CapeCoddah | 16 may 2025 en 12:01

    Estoy de acuerdo con Dominic ya que las advertencias son sólo advertencias. Los resultados de I_Virgo son probablemente porque utilizó el marco de tiempo y el par de divisas equivocado. Desde el informe de Back Test, de casi 2000 barras, debe haber sido M1 o M5 como el marco de tiempo con un par desconocido.

    Sería bueno si MQ añadiera el marco de tiempo y el par de divisas o pares y también separara los resultados de los pares en más detalles en el informe de la prueba retrospectiva para que pudiéramos replicar más de cerca los resultados de la prueba retrospectiva del autor, así como determinar su aplicabilidad en los pares de divisas. Además, sería extremadamente útil si el EA pudiera publicar texto en el gráfico mientras se está ejecutando.


    Yo también creo que es un gran artículo y el plan para estudiarlo a fondo en previsión de la adaptación de sus técnicas a otras EAs


    CapeCoddah

    Shashin Wijewardhane
    Shashin Wijewardhane | 11 jul 2025 en 08:40
    //+------------------------------------------------------------------+
    //| Clase base para todos los algoritmos de ejecución|
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;           // Símbolo comercial
       double            m_totalVolume;      // Volumen total a ejecutar
       double            m_executedVolume;   // Volumen ya ejecutado
       double            m_remainingVolume;  // Volumen restante por ejecutar
       datetime          m_startTime;        // Hora de inicio de la ejecución
       datetime          m_endTime;          // Hora final de ejecución
       int               m_slippage;         // Deslizamiento permitido en puntos
       bool              m_isActive;         // ¿Está activo el algoritmo?
       
       // Estadísticas
       double            m_avgExecutionPrice; // Precio medio de ejecución
       int               m_totalOrders;       // Número total de pedidos realizados
       int               m_filledOrders;      // Número de pedidos ejecutados
       
    public:
       // Constructor
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
       
       // Destructor
       virtual ~CExecutionAlgorithm();
       
       // Métodos virtuales a implementar por las clases derivadas
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate() = 0;
       
       // Métodos comunes
       bool              IsActive() { return m_isActive; }
       double            GetExecutedVolume() { return m_executedVolume; }
       double            GetRemainingVolume() { return m_remainingVolume; }
       double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
       
       // Métodos auxiliares
       bool              PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0);
       bool              ModifyOrder(ulong ticket, double price, double sl, double tp);
       bool              CancelOrder(ulong ticket);
       void              UpdateAverageExecutionPrice(double price, double volume);
       
       // Método auxiliar para obtener el modo de llenado apropiado
       ENUM_ORDER_TYPE_FILLING GetFillingMode();
    };
    
    //+------------------------------------------------------------------+
    //| Constructor|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::CExecutionAlgorithm(string symbol, double volume, 
                                           datetime startTime, datetime endTime, 
                                           int slippage)
    {
       m_symbol = symbol;
       m_totalVolume = volume;
       m_executedVolume = 0.0;
       m_remainingVolume = volume;
       m_startTime = startTime;
       m_endTime = endTime;
       m_slippage = slippage;
       m_isActive = false;
       
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
    }
    
    //+------------------------------------------------------------------+
    //| Destructor|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::~CExecutionAlgorithm()
    {
       // Limpiar los recursos si es necesario
    }
    
    //+------------------------------------------------------------------+
    //| Inicializar el algoritmo|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::Initialize()
    {
       // Validar entradas
       if(m_symbol == "" || m_totalVolume <= 0.0)
       {
          Print("Invalid inputs for execution algorithm");
          return false;
       }
       
       // Comprobar si el símbolo existe
       if(!SymbolSelect(m_symbol, true))
       {
          Print("Symbol not found: ", m_symbol);
          return false;
       }
       
       // Restablecer estadísticas
       m_executedVolume = 0.0;
       m_remainingVolume = m_totalVolume;
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Obtener el modo de relleno apropiado para el símbolo |
    //+------------------------------------------------------------------+
    ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
    {
       // Obtener modos de llenado de símbolos
       int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
       
       // Compruebe los modos de llenado disponibles por orden de preferencia
       if((filling_modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
          return ORDER_FILLING_FOK;
       else if((filling_modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
          return ORDER_FILLING_IOC;
       else
          return ORDER_FILLING_RETURN;
    }
    
    //+------------------------------------------------------------------+
    //| Hacer un pedido|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
    {
       // Validar entradas
       if(volume <= 0.0)
       {
          Print("Invalid order volume");
          return false;
       }
       
       // Preparar la solicitud
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.deviation = m_slippage;
       request.magic = 123456; // Número mágico de identificación
       
       // Establecer la acción y el precio adecuados en función del tipo de orden
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          // Orden de mercado
          request.action = TRADE_ACTION_DEAL;
          request.type_filling = GetFillingMode();
          
          if(orderType == ORDER_TYPE_BUY)
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_ASK);
          else
             request.price = SymbolInfoDouble(m_symbol, SYMBOL_BID);
       }
       else
       {
          // Orden pendiente
          request.action = TRADE_ACTION_PENDING;
          if(price <= 0.0)
          {
             Print("Price must be specified for pending orders");
             return false;
          }
          request.price = price;
       }
       
       // Enviar el pedido
       if(!OrderSend(request, result))
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Comprobar el resultado
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       // Actualizar estadísticas
       m_totalOrders++;
       
       // Para las órdenes de mercado, actualizar inmediatamente las estadísticas de ejecución
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          m_filledOrders++;
          UpdateAverageExecutionPrice(request.price, volume);
          m_executedVolume += volume;
          m_remainingVolume -= volume;
       }
       
       Print("Order placed successfully. Ticket: ", result.order, " Volume: ", volume, " Price: ", request.price);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Modificar un pedido existente|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
    {
       // Preparar la solicitud
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_MODIFY;
       request.order = ticket;
       request.price = price;
       request.sl = sl;
       request.tp = tp;
       
       // Enviar la solicitud de modificación
       if(!OrderSend(request, result))
       {
          Print("OrderModify error: ", GetLastError());
          return false;
       }
       
       // Comprobar el resultado
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderModify failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order modified successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Cancelar un pedido existente|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::CancelOrder(ulong ticket)
    {
       // Preparar la solicitud
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_REMOVE;
       request.order = ticket;
       
       // Enviar la solicitud de anulación
       if(!OrderSend(request, result))
       {
          Print("OrderCancel error: ", GetLastError());
          return false;
       }
       
       // Comprobar el resultado
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderCancel failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       Print("Order cancelled successfully. Ticket: ", ticket);
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Actualizar el precio medio de ejecución|
    //+------------------------------------------------------------------+
    void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
    {
       // Calcular el nuevo precio medio de ejecución
       if(m_executedVolume > 0.0)
       {
          // Media ponderada de los precios antiguos y nuevos
          m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                               (m_executedVolume + volume);
       }
       else
       {
          // Primera ejecución
          m_avgExecutionPrice = price;
       }
    }
    //+------------------------------------------------------------------+
    Análisis cuantitativo de tendencias: Recopilamos estadísticas en Python Análisis cuantitativo de tendencias: Recopilamos estadísticas en Python
    ¿Qué es el análisis cuantitativo de tendencias en el mercado Forex? Recopilamos estadísticas sobre las tendencias, su magnitud y distribución en el par de divisas EURUSD. Cómo el análisis cuantitativo de tendencias puede ayudarle a crear un asesor comercial rentable.
    Modelos ocultos de Márkov en sistemas comerciales de aprendizaje automático Modelos ocultos de Márkov en sistemas comerciales de aprendizaje automático
    Los modelos ocultos de Márkov (HMM) son una potente clase de modelos probabilísticos diseñados para analizar datos secuenciales, donde los eventos observados dependen de alguna secuencia de estados no observados (ocultos) que forman un proceso de Márkov. Los principales supuestos del HMM incluyen la propiedad de Márkov para estados ocultos, lo que significa que la probabilidad de transición al siguiente estado depende solo del estado actual y la independencia de las observaciones dado el conocimiento del estado oculto actual.
    Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
    En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
    Automatización de estrategias de trading en MQL5 (Parte 17): Dominar la estrategia de scalping Grid-Mart con un panel de control dinámico Automatización de estrategias de trading en MQL5 (Parte 17): Dominar la estrategia de scalping Grid-Mart con un panel de control dinámico
    En este artículo, exploramos la estrategia de scalping Grid-Mart, automatizándola en MQL5 con un panel de control dinámico para obtener información comercial en tiempo real. Detallamos su lógica martingala basada en cuadrículas y sus características de gestión de riesgos. También guiamos en las pruebas retrospectivas y la implementación para obtener un rendimiento sólido.