English
preview
Передовые алгоритмы исполнения ордеров на MQL5: TWAP, VWAP и ордера Iceberg

Передовые алгоритмы исполнения ордеров на MQL5: TWAP, VWAP и ордера Iceberg

MetaTrader 5Торговые системы |
117 6
N Soumik
N Soumik
  1. Введение
  2. Ознакомление с алгоритмами исполнения
  3. Реализация средствами MQL5
  4. Реализация анализатора эффективности
  5. Сравнение эффективности алгоритмов
  6. Интеграция алгоритмов исполнения с торговыми стратегиями
  7. Примеры интеграции
  8. Интегрированная стратегия
  9. Результаты тестирования на истории
  10. Заключение



Введение

Представьте, что вы стоите на краю торгового зала и ваше сердце колотится, когда вы следите за изменениями цен в реальном времени. Одно неверное движение, один слишком большой ордер — и ваше преимущество испарится в мгновение ока. Добро пожаловать в мир, где качество исполнения — это не просто приятный бонус, а секретное оружие, отделяющее победителей от остальных.

На протяжении десятилетий институциональные тяжеловесы тихо использовали сложные алгоритмы, чтобы разрезать, фильтровать и скрытно развертывать свои ордера, чтобы избегать проскальзывания и сдерживать влияние рынка. Теперь, благодаря гибкости MQL5, этот же эффективный набор сценариев доступен каждому амбициозному розничному трейдеру.

В чем же дело?

Представьте себе: вы замечаете прекрасную возможность и решаете пойти по крупному пути. Вы размещаете рыночный ордер на полную сумму, но только для того, чтобы наблюдать, как цена падает под тяжестью вашей собственной сделки. За считанные секунды ваш идеальный вход становится шатким компромиссом. Это печально известный тормоз рыночного влияния, и он кусается даже на самых ликвидных площадках.

Алгоритмы исполнения — ваше противоядие. Разбивая большой заказ на последовательность более мелких, стратегически рассчитанных по времени частей, они сглаживают ваш след в книге ордеров. Каков результат? Меньше проскальзывания, более плотное исполнение и общее улучшение средней цены исполнения.

Из башен из слоновой кости на ваш рабочий стол

«Конечно», — можете вы пожать плечами, — «но я не перемещаю институциональные суммы». Вот в чем загвоздка: вам это делать не обязательно. Независимо от того, используете ли вы половину лота или несколько мини-лотов, волатильность все равно может исказить ваше исполнение. Эти инструменты помогут вам:

  • Укрощение проскальзывания: Даже скромные ордера могут колебаться на нестабильных рынках.
  • Отточите своё преимущество: Многоуровневые исполнения часто дают вам более выгодную среднюю цену, чем одноразовая игра.
  • Оставайтесь в дзен-состоянии: Автоматизированные рабочие процессы исключают соблазн панически покупать или продавать.
  • Масштабируйте плавно: По мере роста вашего счета исполнение остается четким, независимо от того, насколько объемными становятся ваши ордера.
  • Оставайтесь незамеченными: В частности, ордера Iceberg скрывают истинный размер вашего ордера, заставляя любопытные алгоритмы гадать.

Сегодняшняя демократизированная среда означает, что те же технологии исполнения, которые когда-то требовали многомиллионных бюджетов, теперь могут работать на вашей персональной торговой станции. Добавив в свою платформу отточенный код на MQL5 для стратегий TWAP, VWAP и Iceberg, вы вооружитесь институциональной огневой мощью, не покидая при этом сферу розничной торговли.

Приготовьтесь включить сценарий в свой процесс исполнения. Игра меняется и с этими алгоритмами в своём арсенале вы будете играть на победу.


Ознакомление с алгоритмами исполнения

Прежде чем углубляться в детали реализации, важно понять теорию, лежащую в основе каждого алгоритма исполнения, и понять, почему они эффективны в различных рыночных сценариях.

  1. Средневзвешенная по времени цена (Time-Weighted Average Price (TWAP): TWAP — это простой алгоритм исполнения, который делит большой ордер на равные части и отправляет их через фиксированные интервалы времени в течение заданного периода. Его цель — соответствовать средней цене инструмента за этот период.
    • Как это работает:

      • Отправляет ордера через регулярные промежутки времени между началом и окончанием.
      • Обычно использует заказы одинакового размера (хотя вы можете добавить размерам произвольность).
      • Следует заранее определенной временной диаграмме, независимо от движения цен.
      • Равномерно распределяет влияние рынка во времени, сводя проскальзывание к минимуму.

    • Когда использовать:

      • Вам нужна средняя цена исполнения за определенный таймфрейм.
      • Ликвидность стабильна на протяжении всего торгового периода.
      • У вас есть фиксированное время для выполнения своего ордера.
      • Вы предпочитаете простой и предсказуемый подход.

  2. Средневзвешенная цена по объёму Volume-Weighted Average Price (VWAP): VWAP улучшает TWAP за счет взвешивания размеров ордеров в соответствии с ожидаемым объемом. Вместо равных частей он отправляет более крупные сделки, когда объем стремится к росту.
    • Как это работает:

      • Распределяет размер ордера пропорционально историческим паттернам объема.
      • Анализирует прошлые объемы торговли для прогнозирования будущего распределения объемов.
      • В некоторых реализациях может адаптироваться к изменениям объема в реальном времени.
      • Выполняется чаще в периоды пиковой нагрузки по объему для снижения воздействия.

    • Когда использовать:

      • Ваша эффективность оценивается по VWAP.
      • Объем следует прогнозируемому дневному паттерну.
      • Вы торгуете на рынке, где ликвидность меняется в течение сессии.
      • Вы хотите соответствовать естественному течению рынка.

  3. Ордера Iceberg: Ордера типа Iceberg направлены на сокрытие истинного размера крупного ордера. В любой момент времени виден только небольшой «кончик»; как только он заполняется, появляется следующая порция.
    • Как это работает:

      • Отображает только часть общего ордера.
      • Выпускает новые видимые блоки после исполнения каждого «кончика».
      • Можно зафиксировать или рандомизировать видимый размер, чтобы снизить вероятность обнаружения.
      • Часто размещаются как лимитные ордера для лучшего контроля цен.

    • Когда использовать:

      • Необходимо скрыть полный размер вашего ордера.
      • Рынок не очень ликвиден и крупные сделки могут повлиять на цены.
      • Вам надо поддерживать исполнение на определенном ценовом уровне.
      • Вы обеспокоены тем, что другие трейдеры обнаружат ваш ордер и выполнят его раньше других.


Реализация средствами MQL5

    Теперь, когда мы понимаем теорию, лежащую в основе этих алгоритмов исполнения, давайте реализуем их на MQL5. Мы создадим модульную объектно-ориентированный фреймворк, который позволит использовать эти алгоритмы по отдельности или объединять их в единую систему исполнения.

    Базовый класс: CExecutionAlgorithm

    Начнем с определения базового класса, обеспечивающего общую функциональность для всех алгоритмов исполнения:

    //+------------------------------------------------------------------+
    //| 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; }
    };
    

    Метод PlaceOrder особенно важен, поскольку он обрабатывает фактическое исполнение ордера и обновляет отслеживание объема:

    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;
    }
    

    Эта функция создает и отправляет рыночный ордер путем нулевой инициализации MqlTradeRequest и MqlTradeResult, заполнения символа, объема, типа ордера, цены, проскальзывания и магического числа, а затем вызова OrderSend. Если отправка не удалась или код возврата брокера не TRADE_RETCODE_DONE, он регистрирует ошибку и возвращает значение false. В случае успеха обновляет внутренние счетчики (общее количество/количество заполнений, выполненный и оставшийся объем), пересчитывает среднюю цену, фиксирует идентификатор тикета и возвращает значение true.

    Реализация TWAP

    Алгоритм TWAP делит период исполнения на равные временные интервалы и размещает ордера одинакового (или случайного) размера на каждом интервале:

    //+------------------------------------------------------------------+
    //| 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();
    };
    

    Ключевой метод: CalculateNextExecutionTime

    Этот метод обеспечивает надлежащее распределение ордеров по времени с первоначальной задержкой для первого ордера:
    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;
    }
    

    Этот метод разбивает окно от m_startTime до m_endTime на равные сегменты m_intervals и возвращает момент, когда должна сработать следующая сделка: при самом первом вызове это просто m_startTime + m_initialDelay, а при каждом последующем вызове это TimeCurrent() + один интервал в секундах (но никогда не больше m_endTime).

    Метод Execute:Метод Execute проверяет, настало ли время разместить ордер, и обрабатывает фактическое размещение заказа:

    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;
    }
    

    Этот метод Execute управляет одним фрагментом работы вашего TWAP. Во-первых, он прерывается, если стратегия не активна или еще не настало время торговать. При этом он выбирает либо фиксированный, либо случайный фрагмент оставшегося объема (никогда не превышающий того, что осталось), а затем ищет текущий спрос (для покупки) или предложение (для продажи). Он регистрирует интервал, объем и цену, создает MqlTradeRequest с вашим символом, объемом, типом, ценой, проскальзыванием и магическим числом, а затем вызывает OrderSend. Если отправка не удалась или брокер возвращает что-либо, кроме TRADE_RETCODE_DONE, он выводит ошибку и возвращает значение false.

    В случае успеха увеличивает счетчики ордеров, корректирует выполненный и оставшийся объем, увеличивает счетчик интервалов, отмечает, что первый ордер был отправлен, а затем либо планирует время следующего исполнения, либо деактивирует стратегию, если у вас закончились интервалы или объем. Наконец, он регистрирует произошедшее и возвращает значение true.

    Реализация VWAP

    Алгоритм VWAP похож на TWAP, но распределяет размеры ордеров на основе исторических паттернов объема:
    //+------------------------------------------------------------------+
    //| 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();
    };
    
    Как и в случае с TWAP, VWAP также реализует метод CalculateNextExecutionTime для обеспечения надлежащего интервала между ордерами:
    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;
    }
    

    Реализация ордеров Iceberg

    Ордера типа Iceberg скрывают истинный размер ордера, выставляя на рынок в любой определенный момент времени лишь небольшую его часть:

    //+------------------------------------------------------------------+
    //| 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; }
    };
    
    Метод Execute размещает новую видимую часть ордера:
    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;
    }
    

    При запуске Execute сначала проверяется активность алгоритма. Если уже есть активный дочерний ордер типа iceberg, он вызывает CheckAndReplaceOrder(), чтобы проверить, требуется ли его отмена или пополнение. В противном случае он выбирает видимый фрагмент (фиксированный или случайный), ограничивает его оставшейся суммой и регистрирует размер и цену.

    Затем он создает запрос отложенного ордера (TRADE_ACTION_PENDING) с символом, объемом, предельной ценой, проскальзыванием и магическим числом и вызывает OrderSend. В случае ошибки или невыполнения кода возврата он регистрирует и возвращает значение false; в случае успеха он сохраняет новый тикет, отмечает заказ активным, записывает время размещения, регистрирует данные и возвращает значение true.

    Метод Update включает обнаружение таймаута ордера, чтобы гарантировать, что ордера не останутся активными бесконечно:

    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;
    }
    

    Update периодически опрашивает рынок и статус ордеров с интервалами, определяемыми функцией m_checkInterval. Если весь объем исчерпан, он завершается. В противном случае, как только наступит время проверки, он регистрирует текущую цену спроса/предложения и лимитную цену. Если ордер активен, он проверяется на таймаут: если ордер истек, он отменяется, сбрасывает состояние, переходит в режим m_orderPlacementDelay и повторно выполняется для размещения нового фрагмента; если таймаут не истек, он откладывается в CheckAndReplaceOrder(). Если активного ордера нет, он просто вызывает Execute для отправки следующей видимой порции.


    Реализация анализатора эффективности

    Наш класс анализатора эффективности отслеживает эти показатели и предоставляет методы для анализа и сравнения работы алгоритмов:
    //+------------------------------------------------------------------+
    //| 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 объединяет все ваши пост-торговые показатели в одном месте. При его построении вы присваиваете ему символ и контрольную цену на момент принятия решения; в качестве начала отсчета он отмечает текущее время. По мере наполнения каждого дочернего ордера вы вызываете RecordExecution(время, цена, объем), который обновляет текущие итоги — совокупный объем, средневзвешенную цену исполнения и временные метки. После завершения стратегии (или периодически) вы вызываете CalculateMetrics(), который вычисляет:

    • Дефицит реализации (разница в прибылях и убытках между ценой вашего решения и фактическим исполнением),
    • Среднее проскальзывание по сравнению с котируемыми ценами,
    • Оценочное влияние вашего присутствия на рынке,
    • Общее время выполнения (конец минус начало),
    • Улучшение цен (если таковое имеется) по сравнению с эталонными показателями.

    Вы даже можете сравнить два прогона с помощью CompareAlgorithms(otherAnalyzer), чтобы увидеть, какая стратегия показала лучшие результаты. Наконец, PrintReport() выводит ключевые статистические данные в лог для быстрого просмотра, а SaveReportToFile(filename) позволяет сохранить полный отчет во внешнем хранилище. Легкие методы-получатели предоставляют доступ к каждой метрике для пользовательских информационных панелей или для дальнейшего анализа.


    Сравнение эффективности алгоритмов

    Различные рыночные условия благоприятствуют различным алгоритмам исполнения. Вот общее сравнение, т.е. практическое правило:
    1. TWAP:
      • Наилучшим образом подходит для: Стабильных рынков с постоянной ликвидностью
      • Преимущества: Простой, предсказуемый исполнительный паттерн
      • Минусы: Не адаптируется к изменяющимся рыночным условиям
    2. VWAP:
      • Наилучшим образом подходит для: Рынки с предсказуемыми паттернами объема
      • Преимущества: Соответствует естественному ритму рынка, часто достигает более выгодных цен
      • Минусы: Требуется объем исторических данных, более сложная реализация
    3. Ордера Iceberg:
      • Наилучшим образом подходит для: Менее ликвидные рынки или когда чувствительность цен высокая
      • Преимущества: Минимизирует влияние на рынок, сохраняет контроль над ценами
      • Минусы: Время исполнения может быть непредсказуемым, риск частичного исполнения


    Интеграция алгоритмов исполнения с торговыми стратегиями

    Настоящая сила этих алгоритмов исполнения раскрывается при их интеграции с торговыми стратегиями. В данном разделе показано, как интегрировать наши алгоритмы исполнения в комплексные торговые системы.

    Диспетчер

    Чтобы упростить интеграцию, мы создадим класс диспетчер (Execution Manager), который будет служить фасадом для всех наших алгоритмов выполнения:

    //+------------------------------------------------------------------+
    //| 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 действует как простой фасад для любого из ваших алгоритмов исполнения и объединяет их в единый торговый процесс. Внутри он хранит указатель на текущий выбранный CExecutionAlgorithm (TWAP, VWAP или Iceberg), а также CPerformanceAnalyzer для отслеживания того, насколько хорошо выполняются ваши ордера.

    Вы выбираете свою стратегию, вызывая один из методов Create…, передавая символ, общий объем, время начала/окончания (для TWAP/VWAP), количество интервалов или размеры фрагментов, тип ордера и любые специфичные для алгоритма параметры (рандомизация, окно истории, предельная цена и т. д.). После создания вы проводите его через обычный жизненный цикл:

    1. Initialize() настраивает любое необходимое вам состояние или данные.
    2. Execute() запускает следующий фрагмент или дочерний ордер.
    3. Update() опрашивает заполнения, рыночные данные или таймауты.
    4. Terminate() выполняет очистку, как только вы все заполнили или хотите остановиться.

    Если вы включите анализ работы с помощью EnablePerformanceAnalysis(), диспетчер будет регистрировать цены исполнения в сравнении с контрольным показателем при принятии решения и вы сможете вывести краткий отчет о прибылях/убытках и проскальзывании с помощью PrintPerformanceReport(). Вы всегда можете получить необработанный объект алгоритма с помощью GetAlgorithm() для пользовательского зондирования или измерения.


    Примеры интеграции

    Ниже приведены примеры того, как интегрировать наши алгоритмы исполнения с различными типами торговых стратегий:

    1. Стратегия следования за трендом с исполнением 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. Стратегия возврата к среднему с исполнением 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. Стратегия пробоя с помощью ордеров 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.");
               }
            }
         }
      }
      

    Во всех трех примерах интеграция следует одному и тому же паттерну высокого уровня:

    1. Поддержание состояния

      • Логическое значение inPosition отслеживает, заполняете ли вы в данный момент ордер.
      • Статический диспетчер CExecutionManager существует на протяжении тиков и управляет жизненным циклом выбранного вами алгоритма.

    2. Логика входа

      • После вашего входного сигнала (пересечение скользящих средних, пороговое значение RSI, прорыв канала) вызовите соответствующий метод создания в executionManager (TWAP, VWAP или Iceberg), передающий символ, общий объем, временное окно или лимитную цену, параметры фрагментов и тип ордера.
      • Если создание прошло успешно, немедленно вызовите executionManager.Initialize(), установите inPosition=true и зарегистрируйте начало работы.

    3. Непрерывное исполнение

      • Пока inPosition имеет значение true, каждый OnTick() вызывает executionManager.Update().
      • Внутри Update() диспетчер будет по мере необходимости вызывать Execute(), опрашивать заполнения, обрабатывать таймауты или обновления рынка, а также планировать следующий фрагмент (или отменять/заменять дочерние ордера для Iceberg).

    4. Завершение и выход

      • После каждого обновления проверяйте executionManager.GetAlgorithm()->IsActive(). Как только он вернет значение false (все интервалы выполнены или объем исчерпан), установите inPosition=false и запишите в лог, что выполнение завершено.
      • В примере возврата к среднему значению VWAP есть дополнительная проверка выхода: если цена разворачивается и выходит за пределы порогового значения RSI в середине исполнения, для ранней остановки вызывайте executionManager.Terminate() .

    Правила для каждого из них следующие:
    1. Следование за трендом + TWAP
      Вход: Пересечение быстрой скользящей средней над медленной скользящей средней активирует
      Исполнение: Разделяет вашу покупку 1 лота на 5 равных частей в течение следующего часа

    2. Возврат к среднему + VWAP
      Вход: RSI < 30 для сделок на покупку, > 70 для сделок на продажу
      Исполнение: Распределяет 1 лот против исторического объема на 5 частей в течение 1 часа.
      Ранний выход: Если сигнал меняется (например, RSI > 70 во время сделки на покупку), executionManager.Terminate() прерывает оставшиеся части

    3. Пробой + Iceberg
      Вход: Цена пробивает максимум (покупка) или минимум (продажа) канала.
      Исполнение: Устанавливает отложенный лимит на уровне цены прорыва, открывая только ~0,1 лота за раз и пополняя его до тех пор, пока не будет заполнен 1 лот целиком.

    Заменив CreateTWAP, CreateVWAP или CreateIcebergOrder, вы подключаете любой алгоритм исполнения к своей логике сигнала, не дублируя шаблон управления ордерами.


    Интегрированная стратегия

    Полный код:

    //+------------------------------------------------------------------+
    //|                                  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);
          }
       }
    }
    //+------------------------------------------------------------------+
    

    Советник IntegratedStrategy.mq5 начинается с объявления своих метаданных (авторское право, ссылка, версия) и включения заголовков для всех наших классов алгоритма исполнения и анализатора работы. Затем он определяет четыре настраиваемых пользователем входа: периоды быстрой и медленной простых скользящих средних, общий объем торговли на сигнал и логический флаг для переключения на «адаптивное» исполнение (где диспетчер решает, использовать ли TWAP, VWAP или Iceberg). Также объявлены глобальный указатель на CExecutionManager и два хэндла индикатора, чтобы они сохранялись между тиками.

    В OnInit() мы инстанцируем диспетчер исполнения, передавая ему текущий символ, максимум из трёх параллельных алгоритмов и наш адаптивный флаг, а затем создаем два хэндла индикатора SMA. Если какой-либо из хэндлов дает сбой, инициализация прерывается. OnDeinit() просто очищает данные, удаляя диспетчера и освобождая хэндлы индикаторов, гарантируя отсутствие утечек памяти или хэндлов при удалении советника или завершении работы платформы.

    Основная логика находится в OnTick(). Сначала мы вызываем UpdateAlgorithms() в диспетчере исполнения, чтобы любые существующие дочерние ордера (фрагменты TWAP, интервалы VWAP или ветви Iceberg) обрабатывались, отменялись или пополнялись по мере необходимости. Затем ждем новый бар (пропуская его, если тиковый объем еще нарастает). После открытия бара мы вытаскиваем последние два значения SMA для быстрого и медленного периодов. Пересечение снизу вверх вызывает сигнал на покупку; обратное — на продажу.

    Если адаптивное выполнение включено, мы передаем сигнал и объём в g_executionManager.ExecuteSignal(), чтобы он выбрал подходящий алгоритм. В противном случае мы вручную запускаем экземпляр TWAP на один час и шесть фрагментов. Этот шаблон четко разделяет логику входа (обнаружение тренда) от логики управления ордерами, позволяя одному и тому же фасаду управлять множеством стилей исполнения без дублирования шаблона.

    После включения Take Profit код меняется на:

    //+------------------------------------------------------------------+
    //|                                  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);
            }
        }
    }
    //+------------------------------------------------------------------+
    


    Результаты тестирования на истории

    1. Кривые капитала и баланса

    Зеленая лесенка «Balance» отображает балансовый капитал вашего счета всякий раз, когда советник закрывает позицию; синяя линия «Equity» сглаживает нереализованные прибыли и убытки между сделками. Мы можем наблюдать четкую тенденцию к росту с января по начало марта с несколькими откатами — каждый откат достигает максимума около 10–16%, прежде чем следующая серия выигрышей восстановит прирост. Этот паттерн предполагает, что система процветает в трендовых условиях, но все же испытывает терпимые падения капитала.

    2. Объем и использование риска

    Внизу треугольники «Нагрузка на депозит» (Deposit Load) постепенно уменьшаются с течением времени — это размер вашей позиции в процентах от капитала. Он начинается примерно с 10% от вашего баланса и уменьшается по мере роста вашего капитала (при фиксированном размере объема), то есть наш риск на сделку фактически уменьшается по мере роста счета. Вот почему просадки остаются пропорционально схожими, даже если ваш долларовый капитал растет.

    3. Ключевые показатели прибыльности

    • Начальный депозит: $1 000

    • Чистая прибыль: + $703 (доходность 70% за ~2 месяца)

    • Коэффициент прибыльности: 2,34 (вы зарабатываете 2,34 доллара за каждый потраченный 1 доллар)

    • Ожидаемая прибыль: в среднем 2,34 доллара за сделку

    • Коэффициент Шарпа: 5,47 (очень высокий — высокая доходность с поправкой на риск)

    Эти цифры говорят нам, что стратегия не только прибыльна, но и обеспечивает существенный буфер над собственной волатильностью.

    4. Просадка и восстановление

    • Максимальная просадка баланса: 156 пунктов или 9.99%

    • Максимальная просадка капитала: 228 пунктов или 15.89%

    • Фактор восстановления: 3.08 (чистая прибыль ÷ максимальная просадка)

    Фактор восстановления выше 2 обычно считается хорошим, так что при 3,08 вы получаете прибыль, в три раза превышающую ваш наибольший убыток.

    5. Распределение сделок

    • Всего сделок: 300 (600 сделок, поэтому каждый вход+выход считается за два)

    • Коэффициент выигрыша: 76% (228 выигрышей против 72 проигрышей)

    • Средний выигрыш: $5,39

    • Средний убыток: – $7,31

    Несмотря на то, что ваш процент выигрышных сделок и коэффициент прибыли высоки, обратите внимание, что в среднем ваши убыточные сделки больше выигрышных — на это стоит обратить внимание, если рыночные условия изменятся.

    6. Серии и согласованность

    • Максимальное количество прибыльных сделок подряд: 87 сделок, + $302

    • Максимальное количество убыточных сделок подряд: 23 сделок, – $156

    • Средняя прибыльная серия: 57 сделок

    • Средняя убыточная серия: 18 сделок

    Длительные прибыльные серии ведут к росту, в то время как самая длительная серия проигрышей по-прежнему стоит всего около 15% капитала.


    Заключение

    Представьте, что вы — индивидуальный трейдер на переполненной рыночной арене — каждый тик имеет значение, каждая цена исполнения предсказывает прибыль или убыток. Включив в свой инструментарий ордера TWAP, VWAP и Iceberg, вы больше не просто реагируете на колебания цен; вы ими дирижируете. Эти некогда элитные алгоритмы институционального уровня теперь у вас под рукой, разрезая ликвидность словно лазер и превращая хаотичные стаканы цен в возможности.

    TWAP станет вашим постоянным метрономом, равномерно регулируя размер в заданном интервале — идеально, когда течение спокойное и вам просто нужна плавная поездка. VWAP превращает вас в опытного трейдера, отслеживающего самые напряженные торговые события дня и чувствующего пульс рынка. А когда вам нужно скрыть свои намерения, ордера Iceberg скрывают ваши истинные размеры под поверхностью, открывая ровно столько, сколько нужно для выполнения, не отпугивая при этом крупных игроков.

    Но это не просто отдельные приемы. С помощью нашей модульной платформы MQL5 вы можете интегрировать их в любую стратегию — будь то следование за трендом, возврат к среднему значению или охота за пробоями — с легкостью переключаясь на новый подход. Единый фасад ExecutionManager позволяет вам менять, объединять или даже накладывать алгоритмы во время торговли, в то время как PerformanceAnalyzer ведет счет как ястреб, измеряя проскальзывание, дефицит и влияние на рынок с точностью до последнего пипса.

    Что будет дальше? Подумайте об исполнении как о живом существе, которое адаптируется. Позвольте вашему TWAP учиться на скачках волатильности. Направьте фрагменты VWAP в самые глубокие водоёмы. Научите свой Iceberg чувствовать, где затаились хищники и прятаться глубже. И зачем останавливаться на достигнутом? Внедрите машинное обучение, чтобы предсказать идеальную микросекунду для срабатывания, или смешайте типы ордеров в индивидуальные гибриды, которые соответствуют вашему уникальному преимуществу.

    Мир трейдинга никогда не стоит на месте и исполнение ваших ордеров тоже не должно останавливаться. Погружайтесь, смело экспериментируйте и превратите каждую часть, каждое выполнение в рассчитанное преимущество. Ваша сильная сторона ждет в коде.

    Для удобства приводим краткий обзор файлов, включенных в эту статью:

    Название файла Описание
    ExecutionAlgorithm.mqh Базовый класс для всех алгоритмов исполнения 
    TWAP.mqh Реализация средневзвешенной по времени цены
    VWAP.mqh Реализация средневзвешенной цены по объему
    IcebergOrder.mqh Реализация ордеров Iceberg
    PerformanceAnalyzer.mqh Инструменты для анализа эффективности исполнения
    ExecutionManager.mqh Фасад для легкой интеграции с торговыми стратегиями
    IntegratedStrategy.mq5
    Пример советника, демонстрирующий интеграцию с торговой стратегией
    IntegratedStrategy - Take Profit.mq5 Пример советника, демонстрирующий интеграцию с торговой стратегией с тейк-профитом и стоп-лоссом в процентах от баланса счета
    Включая эти передовые алгоритмы исполнения в свой торговый инструментарий, вы делаете значительный шаг к более профессиональной и эффективной торговле. Независимо от того, хотите ли вы минимизировать влияние крупных сделок, улучшить средние цены исполнения или просто усовершенствовать свой подход к торговле, эти алгоритмы предоставляют ценные решения, которые могут повысить эффективность вашей торговли на современных конкурентных рынках.

    Перевод с английского произведен MetaQuotes Ltd.
    Оригинальная статья: https://www.mql5.com/en/articles/17934

    Прикрепленные файлы |
    TWAP.mqh (9.35 KB)
    VWAP.mqh (15.91 KB)
    IcebergOrder.mqh (15.33 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (6)
    i_vergo
    i_vergo | 16 мая 2025 в 07:44
    Отличная статья
    Тестировал ваш алгоритм.
    В файле ExecutionAlgorithm.mqh добавил эту строку request.type_filling = ORDER_FILLING_IOC; при выставлении ордера, чтобы исправить проблему с выставлением ордера.
    Протестировал на M5, открыта только 1 сделка за 2 месяца, частичных ордеров не открывалось.
    Протестировал на H1, не применил ни SL, ни TP, все сделки закрылись в убыток.

    также при компиляции выдает предупреждения
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 271 41
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 272 22
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 449 17
    возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer.mqh 222 17
    возможная потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager.mqh 418 17


    подскажите, как протестировать algo,
    временные рамки. и любые другие рекомендации.
    i_vergo
    i_vergo | 16 мая 2025 в 07:57
    исправить предупреждения
    также при компиляции выдает предупреждения
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 271 41
    Возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 272 22
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP .mqh 449 17
    возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer .mqh 222 17
    возможна потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager .mqh 418 17


    я изменил строку кода
    m_volumeProfile[intervalIndex] += rates[i].tick_volu

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

    Это устранило предупреждения
    Теперь нужна ваша помощь в отношении других моих запросов, как
    Таймфрейм
    А также
    почему все сделки во время бэктеста приводят к убыткам
    как проверить эту большую работу от вас.
    Dominic Michael Frehner
    Dominic Michael Frehner | 16 мая 2025 в 07:57
    i_vergo возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 271 41
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 272 22
    возможная потеря данных из-за преобразования типа из 'long' в 'double' VWAP.mqh 449 17
    возможная потеря данных из-за преобразования типа из 'long' в 'double' PerformanceAnalyzer.mqh 222 17
    возможная потеря данных из-за преобразования типа из 'long' в 'double' ExecutionManager.mqh 418 17


    подскажите, как протестировать algo,
    временные рамки. и любые другие рекомендации.

    Предупреждения не являются проблемой, но их можно быстро исправить. Но да, было бы здорово, если бы автор пошагово показал, какие настройки и входы он использовал для бэктеста.

    CapeCoddah
    CapeCoddah | 16 мая 2025 в 12:01

    Я согласен с Домиником, поскольку предупреждения - это всего лишь предупреждения. Результаты I_Virgo, вероятно, связаны с тем, что он использовал неправильный таймфрейм и валютную пару. Судя по отчету Back Test из почти 2000 баров, это должен был быть либо M1, либо M5 в качестве таймфрейма с неизвестной парой.

    Было бы неплохо, если бы MQ добавил в отчет Back Test таймфрейм и валютную пару или пары, а также более подробно разделил результаты по парам, чтобы мы могли более точно воспроизвести результаты обратного тестирования автора, а также определить его применимость к парам Форекс. Также было бы очень полезно, если бы советник мог выводить текст на график во время работы.


    Я также считаю, что это отличная статья, и планирую тщательно изучить ее в ожидании адаптации его методов к другим советникам


    CapeCoddah

    Shashin Punyajith Wijewardhane
    Shashin Punyajith Wijewardhane | 11 июл. 2025 в 08:40
    //+------------------------------------------------------------------+
    //| Базовый класс для всех алгоритмов выполнения|
    //+------------------------------------------------------------------+
    class CExecutionAlgorithm
    {
    protected:
       string            m_symbol;           // Торговый символ
       double            m_totalVolume;      // Общий объем для выполнения
       double            m_executedVolume;   // Объем уже выполнен
       double            m_remainingVolume;  // Оставшийся объем для выполнения
       datetime          m_startTime;        // Время начала выполнения
       datetime          m_endTime;          // Время окончания выполнения
       int               m_slippage;         // Допущенное отставание в очках
       bool              m_isActive;         // Активен ли алгоритм в данный момент
       
       // Статистика
       double            m_avgExecutionPrice; // Средняя цена исполнения
       int               m_totalOrders;       // Общее количество размещенных заказов
       int               m_filledOrders;      // Количество выполненных заказов
       
    public:
       // Конструктор
       CExecutionAlgorithm(string symbol, double volume, datetime startTime, datetime endTime, int slippage);
       
       // Деструктор
       virtual ~CExecutionAlgorithm();
       
       // Виртуальные методы, которые должны быть реализованы производными классами
       virtual bool      Initialize();
       virtual bool      Execute() = 0;
       virtual bool      Update() = 0;
       virtual bool      Terminate() = 0;
       
       // Общие методы
       bool              IsActive() { return m_isActive; }
       double            GetExecutedVolume() { return m_executedVolume; }
       double            GetRemainingVolume() { return m_remainingVolume; }
       double            GetAverageExecutionPrice() { return m_avgExecutionPrice; }
       
       // Вспомогательные методы
       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);
       
       // Метод-помощник для получения соответствующего режима заполнения
       ENUM_ORDER_TYPE_FILLING GetFillingMode();
    };
    
    //+------------------------------------------------------------------+
    //| Конструктор|
    //+------------------------------------------------------------------+
    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;
    }
    
    //+------------------------------------------------------------------+
    //| Деструктор|
    //+------------------------------------------------------------------+
    CExecutionAlgorithm::~CExecutionAlgorithm()
    {
       // Очистите ресурсы, если это необходимо
    }
    
    //+------------------------------------------------------------------+
    //| Инициализация алгоритма|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::Initialize()
    {
       // Проверка вводимых данных
       if(m_symbol == "" || m_totalVolume <= 0.0)
       {
          Print("Invalid inputs for execution algorithm");
          return false;
       }
       
       // Проверьте, существует ли символ
       if(!SymbolSelect(m_symbol, true))
       {
          Print("Symbol not found: ", m_symbol);
          return false;
       }
       
       // Сброс статистики
       m_executedVolume = 0.0;
       m_remainingVolume = m_totalVolume;
       m_avgExecutionPrice = 0.0;
       m_totalOrders = 0;
       m_filledOrders = 0;
       
       return true;
    }
    
    //+------------------------------------------------------------------+
    //| Получите соответствующий режим заполнения для символа |
    //+------------------------------------------------------------------+
    ENUM_ORDER_TYPE_FILLING CExecutionAlgorithm::GetFillingMode()
    {
       // Получение режимов заполнения символов
       int filling_modes = (int)SymbolInfoInteger(m_symbol, SYMBOL_FILLING_MODE);
       
       // Проверьте доступные режимы заполнения в порядке предпочтения
       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;
    }
    
    //+------------------------------------------------------------------+
    //| Разместить заказ|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::PlaceOrder(ENUM_ORDER_TYPE orderType, double volume, double price = 0.0)
    {
       // Проверка вводимых данных
       if(volume <= 0.0)
       {
          Print("Invalid order volume");
          return false;
       }
       
       // Подготовьте запрос
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.symbol = m_symbol;
       request.volume = volume;
       request.type = orderType;
       request.deviation = m_slippage;
       request.magic = 123456; // Магическое число для идентификации
       
       // Установите соответствующее действие и цену в зависимости от типа ордера
       if(orderType == ORDER_TYPE_BUY || orderType == ORDER_TYPE_SELL)
       {
          // Рыночный ордер
          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
       {
          // Отложенный ордер
          request.action = TRADE_ACTION_PENDING;
          if(price <= 0.0)
          {
             Print("Price must be specified for pending orders");
             return false;
          }
          request.price = price;
       }
       
       // Отправить заказ
       if(!OrderSend(request, result))
       {
          Print("OrderSend error: ", GetLastError());
          return false;
       }
       
       // Проверьте результат
       if(result.retcode != TRADE_RETCODE_DONE)
       {
          Print("OrderSend failed with code: ", result.retcode, " - ", result.comment);
          return false;
       }
       
       // Обновление статистики
       m_totalOrders++;
       
       // Для рыночных ордеров обновлять статистику выполнения немедленно
       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;
    }
    
    //+------------------------------------------------------------------+
    //| Изменить существующий заказ|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::ModifyOrder(ulong ticket, double price, double sl, double tp)
    {
       // Подготовьте запрос
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_MODIFY;
       request.order = ticket;
       request.price = price;
       request.sl = sl;
       request.tp = tp;
       
       // Отправьте запрос на модификацию
       if(!OrderSend(request, result))
       {
          Print("OrderModify error: ", GetLastError());
          return false;
       }
       
       // Проверьте результат
       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;
    }
    
    //+------------------------------------------------------------------+
    //| Отменить существующий заказ|
    //+------------------------------------------------------------------+
    bool CExecutionAlgorithm::CancelOrder(ulong ticket)
    {
       // Подготовьте запрос
       MqlTradeRequest request;
       MqlTradeResult result;
       ZeroMemory(request);
       
       request.action = TRADE_ACTION_REMOVE;
       request.order = ticket;
       
       // Отправьте запрос на отмену
       if(!OrderSend(request, result))
       {
          Print("OrderCancel error: ", GetLastError());
          return false;
       }
       
       // Проверьте результат
       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;
    }
    
    //+------------------------------------------------------------------+
    //| Обновление средней цены выполнения|
    //+------------------------------------------------------------------+
    void CExecutionAlgorithm::UpdateAverageExecutionPrice(double price, double volume)
    {
       // Рассчитываем новую среднюю цену исполнения
       if(m_executedVolume > 0.0)
       {
          // Средневзвешенное значение старой и новой цен
          m_avgExecutionPrice = (m_avgExecutionPrice * m_executedVolume + price * volume) / 
                               (m_executedVolume + volume);
       }
       else
       {
          // Первое выполнение
          m_avgExecutionPrice = price;
       }
    }
    //+------------------------------------------------------------------+
    Торгуем опционы без опционов (Часть 2): Использование в реальной торговле Торгуем опционы без опционов (Часть 2): Использование в реальной торговле
    В статье рассматриваются простые опционные стратегии и их реализация на MQL5. Пишем базовый эксперт, который будет модернизироваться и усложняться.
    Оптимизация сообществом ученых — Community of Scientist Optimization (CoSO): Теория Оптимизация сообществом ученых — Community of Scientist Optimization (CoSO): Теория
    Секреты эффективной оптимизации торговых стратегий в метаэвристических подходах. Community of Scientist Optimization — новый популяционный алгоритм, вдохновленный механизмами функционирования научного сообщества. В отличие от традиционных природных метафор, CoSO моделирует уникальные аспекты человеческой научной деятельности: публикацию результатов в журналах, конкуренцию за гранты и формирование исследовательских групп.
    Нейросети в трейдинге: Распутывание структурных компонентов (Энкодер) Нейросети в трейдинге: Распутывание структурных компонентов (Энкодер)
    Предлагаем познакомиться с продолжением реализации фреймворка SCNN, который сочетает в себе гибкость и интерпретируемость, позволяя точно выделять структурные компоненты временного ряда. В статье подробно раскрываются механизмы адаптивной нормализации и внимания, что обеспечивает устойчивость модели к изменяющимся рыночным условиям.
    Создание торговой панели администратора на MQL5 (Часть VIII): Панель аналитики Создание торговой панели администратора на MQL5 (Часть VIII): Панель аналитики
    В этой статье мы углубимся в добавление полезных торговых показателей в специализированное окно, интегрированное в панель администратора советника. Основное внимание уделено внедрению MQL5 для разработки аналитической панели. Подчеркивается ценность данных, которые она предоставляет администраторам. Панель в основном играет образовательную роль, позволяя извлекать из процесса разработки ценные уроки, приносящие пользу как начинающим, так и опытным разработчикам. В статье демонстрируются безграничные возможности, которые предлагает данная серия в плане предоставления передовых программных инструментов. Кроме того, мы рассмотрим реализацию классов PieChart и ChartCanvas в рамках продолжающегося расширения возможностей панели администратора.